接口隔离原则(The Interface Segregation Interface)
这个原则用来处理“胖(fat)”接口(类的接口不是内聚的)所具有的缺点。“胖”接口可以分解成多组方法。
考虑一个安全系统,有一些Door对象,可以被加锁和解锁,并且Door对象知道自己开关状态。
public interface Door {public void lock();public void unlock();public boolean isOpen();
}
考虑一个这样的实现,TimedDoor 如果门开着的时间过长,它就会发出警报声。为了做到这一点,TimedDoor 对象需要和另一个名为Timer 的对象交互。
public interface Timer {// 注册超时服务public void register(int timeout, TimerClient client);
}public interface TimerClient {// 超时后执行public void timeout();
}
如果一个对象希望得到超时通知,它可以调用Timer 的register 函数。我们怎样将TimeClient 类
和TimedDoor 类联系起来,才能在超时时通知到TimedDoor 中相应的处理代码呢?如下是一个容易想到的解决方案。
这个方案最主要的问题:现在Door 类依赖TimeClient 了。可是并不是所有种类的Door 都需要定时功能。
分离客户就是分离接口
Door 接口和TimerClient 接口是被完全不同的客户程序使用的。Timer 使用TimerClient,而操作门的类使用Door。既然客户程序是分离的,所以接口也应该保持分离。
客户对接口施加的反作用力
例如,有些Timer 的使用者会注册多个超时通知请求。比如对于TimedDoor 来说。当它检测到门被打开时,会向Timer 发送一个register() 消息,请求一个超时通知。可是,在超时到达前,门关上了,关闭一会儿后又被再次打开。这就导致在原先的超时到达前又注册了一个新的超时请求。最后,最初的超时到达,TimedDoor 的 timeout() 方法被调用。Door 错误地发出了警报。
修复,在每次超时注册中都包含一个唯一的 timeoutID 码,并在调用TimerClient 的timeout() 方法时,再次使用该标识码。
public interface Timer {// 注册超时服务public void register(int timeout, int timeoutID, TimerClient client);
}public interface TimerClient {// 超时后执行public void timeout(int timeoutID);
}
显然,这个改变会影响到TimerClient 的所有使用者。还会影响到Door 以及Door 的所有客户程序。这是僵化性和粘滞性的臭味。
接口隔离原则(ISP)
不应该强迫客户依赖于它们不用的方法。
1、使用委托分离接口
public TimedDoor implements Door {public void doorTimeout(int timeoutID);// 其他接口方法实现...
}public class DoorTimeAdapter implements TimerClient {private TimedDoor itsTimeDoor;public DoorTimeAdapter(TimedDoor theDoor) {this.itsTimeDoor = theDoor;}public void timeout(int timeoutID) {itsTimeDoor.doorTimeout(timeoutID);}
}
2、使用多重继承分离接口(推荐)