接口隔离原则(Interface Segregation Principle)
1、接口隔离原则的定义:
第一种定义: 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、接口污染定义:
所谓接口污染就是为接口添加了不必要的职责。在接口中加一个新方法只是为了给实现类带来好处,以减少类的数目。持续这样做,接口就被不断污染,变胖。实际上,类的数目根本不是什么问题,接口污染会带来维护和重用方面的问题。最常见的问题是我们为了重用被污染的接口,被迫实现并维护不必要的方法。因此,我们必须分离客户程序,分离客户程序就是分离接口。
3、分离接口的实现方法:
分离接口的方式一般分为两种:
1) 使用委托分离接口。(Separation through Delegation)
就把请求委托给别的接口的实现类来完成需要的职责,就是适配器模式(Adapter)。
2) 使用多重继承分离接口。(Separation through Multiple Inheritance。)
该方法通过实现多个接口来完成需要的职责。
两种方式各有优缺点,通常我们应该先考虑后一个方案,如果涉及到类型转换时则选择前一个方案。
4、实例
假如有一个Door,有lock,unlock功能,另外,可以在Door上安装一个Alarm而使其具有报警功能。用户可以选择一般的Door,也可以选择具有报警功能的Door。
要遵循ISP设计原则,方案如下:
1、在IAlarm接口定义alarm方法,在IDoor接口定义lock,unlock方法。接口之间无继承关系。CommonDoor实现IDoor接口。
public interface IDoor {
public void lock();
public void unlock();
}
public interface IAlarm {
public void alarm();
}
public class CommonDoor implements IDoor {
public void lock() {
System.out.println("CommonDoor is lock!");
}
public void unlock() {
System.out.println("CommonDoor is unlock!");
}
}
AlarmDoor有2种实现方案:
1)同时实现IDoor和IAlarm接口。
public class AlarmDoor implements IDoor, IAlarm {
public void lock() {
System.out.println("AlarmDoor is lock!");
}
public void unlock() {
System.out.println("AlarmDoor is unlock!");
}
public void alarm() {
System.out.println("AlarmDoor is alarm!");
}
}
2)继承CommonDoor,并实现Alarm接口。该方案是继承方式的Adapter设计模式的实现。
此种方案更具有实用性。
public class AlarmDoor extends CommonDoor implements IAlarm {
public void lock() {
super.lock();
}
public void unlock() {
super.unlock();
}
public void alarm() {
System.out.println("AlarmDoor is alarm!");
}
}
2、采用委让实现
public interface IDoor {
public void lock();
public void unlock();
}
public interface IAlarm {
public void lock();
public void unlock();
public void alarm();
}
public class CommonDoor implements IDoor {
public void lock() {
System.out.println("CommonDoor is lock!");
}
public void unlock() {
System.out.println("CommonDoor is unlock!");
}
}
采用委托的方式即采用对象适配器的方式
public class AlarmDoor implements IAlarm {
private CommonDoor commdoor=new CommonDoor();
public void lock() {
commdoor.lock();
}
public void unlock() {
commdoor.unlock();
}
public void alarm() {
System.out.println("AlarmDoor is alarm!");
}
}
5、小结
如果已经设计成了胖接口,可以使用适配器模式隔离它。像其他设计原则一样,接口隔离原则需要额外的时间和努力,并且会增加代码的复杂性,但是可以产生更灵活的设计。如果我们过度的使用它将会产生大量的包含单一方法的接口,所以需要根据经验并且识别出那些将来需要扩展的代码来使用它。