Interface Segregation Principle
动机
当我们试图去设计软件应用时,我们要仔细思考如何去抽象一个包含多个子模块的模块。假设模块仅由一个类构成,我们可以用一个接口来实现系统的抽象。不过,假如我们想扩展我们的应用,增加另一个模块,而这个模块只包含原系统的一部分子模块,那我们就被迫地去实现这整个接口,然后编写一些虚假的方法。这种接口我们称为是胖接口 fat interface
或者被污染的接口。拥有一个被污染的接口不是一个好的解决方案,而且可能给系统带来不恰当的行为。
ISP 规定客户端不应被强迫实现他们不需要使用的接口。应优先选择基于方法分组,分别服务各小模块的小型接口,而不是一个大而全的胖接口。
目的
客户端不应被迫依赖他们不必使用的接口。
例子
以下是一个违背 ISP 的例子。 我们有个 Manager
类来管理工人。还有2种类型的工人,一种工作效率达到平均水平,一种工作效率非常高。两种类型的工人每天都需要午休时间吃饭。�现在一些机器人也进入公司,但是这些机器人不需要吃东西也就不需要午休了。一方面 Robot
类需要实现 IWorker
接口,因为他们确实要干活。另一方面,它们不需要实现 IWorker
接口,因为它们不需要吃东西。
这就是为什么在这种情形下 IWorker
被认为是受污染的接口。
如果我们继续保持现有设计,新的 Robot
类就被迫要实现 eat
方法了。我们也写一个什么也不做的伪类(比如说每天1秒的午休), 然后将给应用带来非预期的影响(比如说经理们看到的午休报告会多于实际的人数)。
根据 ISP ,灵活的设计中不会存在被污染的类。在以上这个情况,接口 IWorker
应拆成2个不同的小接口。
interface IWorker{
public void work();
public void eat();
}
class Worker implements IWorker{
public void work() {
// ... working
}
public void eat() {
// ... eating in lunch break
}
}
class SuperWorker implements IWorker{
public void work() {
// ... working much more
}
public void eat() {
// ... eating in lunch break
}
}
class Robot implements IWorker{
public void work() {
// ... working
}
public void eat() {
// ... Robot doesn't need eat !!! 被迫~~~~
}
}
class Manager{
IWorker worker;
public void setWorker(IWorker w){
this.worker = w;
}
public void manage(){
this.worker.work();
}
}
再下面是遵循 ISP 的例子。通过将 IWorker
拆分成2个独立的接口,新 Robot
就不用再被迫实现这个 eat
方法了。同样,如果我们需要给机器人添加另一个功能,比如说,充电,我们只需要再建一个 IRechargeble
接口,并在其中规定 recharge
方法就行。
interface IWorkable {
public void work();
}
interface IFeedable {
public void eat();
}
class Worker implements IWorkable, IFeedable {
public void work() {
// ... working
}
public void eat() {
// ... eating in lunch break
}
}
class SuperWorker implements IWorkable, IFeedable {
public void work() {
// ... working much more
}
public void eat() {
// ... eating in lunch break
}
}
class Robot implements IWorkable {
public void work() {
// ... working
}
}
class Manager {
IWorkable worker;
public void setWorker(IWorkable w) {
this.worker = w;
}
public void manage() {
this.worker.work();
}
}
总结
如果原有设计已经完成,已有的胖接口可以通过适配器模式来进行拆分。
就如其他原则一样 ISP 也需要耗费额外的时间和精力去应用它,也会使代码更加复杂。不过,它也带来更灵活的设计。如果在不必要的地方也去使用 ISP 的话,会导致一堆只有单个方法的接口。所以,ISP 的应用需要基于经验和常识来识别在未来比较有可能需要扩展的代码区域。
上一篇: 依赖倒置原则
下一篇: 单一职责原则
目录: //www.greatytc.com/p/af861220a6cc