在这里插入图片描述

主页传送门:💁 传送

1.定义

       接口隔离原则,英文名为Interface Segregation Principle,又称ISP原则。

Clients should not be forced to depend upon interfaces that they don’t use.

The dependency of one class to another one should depend on the smallest possible interface.

       即:客户端不应该依赖它不需要的接口。类间的依赖关系应该建立在最小的接口上。

2.定义解读

       我们可以把这两个定义概括为一句话:建立单一接口,不要建立臃肿庞大的接口。再通俗一点讲:接口尽量细化,同时接口中的方法尽量少。看到这里大家有可能要疑惑了,这与单一职责原则不是相同的吗?错,接口隔离原则与单一职责的审视角度是不相同的,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少。例如一个接口的职责可能包含10个方法,这10个方法都放在一个接口中,并且提供给多个模块访问,各个模块按照规定的权限来访问,在系统外通过文档约
束“不使用的方法不要访问”,按照单一职责原则是允许的,按照接口隔离原则是不允许的,因为它要求“尽量使用多个专门的接口”。专门的接口指什么?就是指提供给每个模块的都应该是单一接口,提供给几个模块就应该有几个接口,而不是建立一个庞大的臃肿的接口,容纳所有的客户端访问。

3.优点

  • 降低耦合度:将不同的接口进行隔离,使得每个接口只包含必要的方法,降低模块之间的耦合度,提高代码的可维护性和可扩展性。
  • 提高代码质量:当每个类只需要实现必要的方法时,可以减少代码冗余,提高代码质量,降低代码出错率,提高系统的稳定性。
  • 提高代码复用性:接口隔离原则可以促进代码的复用,因为不同的类可以复用同一个接口,而不需要依赖于其他类的实现。

4.案例分析

       我们举一个例子来说明接口隔离原则对我们提出了什么要求。汽车想必大家都见过吧,现在满大街都是,汽车要运行就要加燃料,有烧天然气,氢气,烧油的,烧电的等等,下面我们实现一个加油站工作人员(这里都叫服务员吧,实际加油,加气,充电的场所肯定不在同一个地方,不过未来也可能会整合在一起)给汽车加燃料的场景,给汽车定义三个方法,加油,加气,充电,服务员定义一个加燃料的方法,类图如下:

«interface»
ICar
+aerate()
+refuel()
+charge()
Taxi
+aerate()
+refuel()
+charge()
Tesla
+aerate()
+refuel()
+charge()
Benz
+aerate()
+refuel()
+charge()
HanDmi
+aerate()
+refuel()
+charge()
Waiter
+fueling()

代码如下:

汽车接口(ICar):

public interface ICar {
	
	void aerate();
	
	void refuel();

	void charge();

}

出租车(Taxi):
这里的出租车是烧气的。

public class Taxi implements ICar {
    
    public void aerate() {
		System.out.println("我在加气");
    }

    public void refuel() {

    }
    
    public void charge() {

    }
}

特斯拉(Tesla):
特斯拉毫无疑问是电车。

public class Tesla implements ICar {
    
    public void aerate() {
		
    }

    public void refuel() {

    }
    
    public void charge() {
		System.out.println("我在充电");
    }
}

奔驰车(Benz):
这里奔驰车是烧油的。

public class Benz implements ICar {
    
    public void aerate() {

    }

    public void refuel() {
		System.out.println("我在加油");
    }
    
    public void charge() {

    }
}

比亚迪汉Dmi(HanDmi):
汉Dmi是油电混动的。

public class HanDmi implements ICar {
    
    public void aerate() {

    }

    public void refuel() {
		System.out.println("我在加油");
    }
    
    public void charge() {
		System.out.println("我在充电");
    }
}

服务员(Waiter):

public class Waiter {

	private ICar car;

	public Waiter(ICar car) {
		this.car = car;
	}
    
    public void fueling() {
		car.aerate();
		car.refuel();
		car.charge();
    }
}

最后需要一个场景类(Client)把前面的类串起来:

public class Client {

    public static void main(String[] args) {
    	ICar taxi = new Taxi();
		Waiter zhangsan = new Waiter(taxi);
		zhangsan.fueling();
		
    	ICar hanDmi = new HanDmi();
		Waiter lisi = new Waiter(hanDmi);
		lisi.fueling();
    }
    
}

不知道大家有没有发现上面的汽车(ICar)有很多空实现,这是因为大部分车只使用一到两种燃料,那这种设计方法显然很臃肿,我们尝试修改一下,将汽车接口再细分一下,分为电车,油车,气车(烧气的车简称气车),类图如下:

«interface»
ICar
«interface»
IGasCar
+aerate()
«interface»
IOilCar
+refuel()
«interface»
ITramCar
+charge()
Taxi
+aerate()
Tesla
+charge()
Benz
+refuel()
HanDmi
+refuel()
+charge()
Waiter
+fueling()

代码如下:

汽车接口(ICar):

public interface ICar {

}

气车接口(IGasCar):

public interface IGasCar extends ICar{
	
	void aerate();

}

油车接口(IOilCar):

public interface IOilCar extends ICar{
	
	void refuel();

}

电车接口(ITramCar):

public interface ITramCar extends ICar {

	void charge();

}

出租车(Taxi):
这里的出租车是烧气的。

public class Taxi implements IGasCar {
    
    public void aerate() {
		System.out.println("我在加气");
    }
    
}

特斯拉(Tesla):
特斯拉毫无疑问是电车。

public class Tesla implements ITramCar {
    
    public void charge() {
		System.out.println("我在充电");
    }
}

奔驰车(Benz):
这里奔驰车是烧油的。

public class Benz implements IOilCar {

    public void refuel() {
		System.out.println("我在加油");
    }
    
}

比亚迪汉Dmi(HanDmi):
汉Dmi是油电混动的。

public class HanDmi implements IOilCar,ITramCar {

    public void refuel() {
		System.out.println("我在加油");
    }
    
    public void charge() {
		System.out.println("我在充电");
    }
}

服务员(Waiter):

public class Waiter {

	private IGasCar gasCar;
	
	private IOilCar oilCar;

	private ITramCar tramCar;

	public Waiter(IGasCar gasCar) {
		this.gasCar = gasCar;
	}
	public Waiter(IOilCar oilCar) {
		this.oilCar = oilCar;
	}
	public Waiter(ITramCar tramCar) {
		this.tramCar = tramCar;
	}
    public void aerate() {
		gasCar.aerate();
    }
    
    public void refuel() {
		oilCar.refuel();
    }
    
    public void charge() {
		tramCar.charge();
    }
    
}

最后需要一个场景类(Client)把前面的类串起来:

public class Client {

    public static void main(String[] args) {
    	IGasCar taxi = new Taxi();
		Waiter zhangsan = new Waiter(taxi);
		zhangsan.aerate();
		
    	ITramCar tesla = new Tesla();
		Waiter lisi = new Waiter(tesla);
		lisi.charge();
    }
    
}

经过优化之后的设计就符合接口隔离原则,气车,油车,电车互相独立,互不影响,生产汽车的厂商也可以随意按照一种或多种接口标准去实现

5.个人总结

       接口隔离原则是对接口的定义,同时也是对类的定义,接口和类尽量使用原子接口或原子类来组装。关于原子该怎么划分,在实践中可以根据以下几个规则来衡量:

  1. 一个接口只服务于一个子模块或业务逻辑;
  2. 通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量让接口达到“满身筋骨肉”,而不是“肥嘟嘟”的一大堆方法;
  3. 已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理;
  4. 每个项目或产品都有特定的环境因素,环境不同,接口拆分的标准就不同。深入了解业务逻辑,根据经验和常识决定接口的粒度大小。接口粒度太小,导致接口数据剧增,开发人员呛死在接口的海洋里;接口粒度太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。

如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏 🙌一起讨论
你的支持就是我✍️创作的动力! 💞💞💞

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐