2.5 接口隔离原则
2.5.1 接口隔离原则定义
《代码整洁之道》的作者Robert C.Martin于2002年给“接口隔离原则”的定义是:客户端不应该被迫依赖于它不使用的方法(Clients should not be forced to depend on methods they do not use)。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
接口隔离原则(Interface Segregation Principle,ISP)要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。
接口隔离是为了高内聚、低耦合。在实际的业务开发中,通常会先定义好需要开发的接口,并由各个服务类实现。但如果没有经过考虑和设计,就很可能造成一个接口中包括众多的接口方法,而这些接口并不一定在每一个类中都需要实现。这样的接口很难维护,也不易于扩展,每一次修改验证都有潜在的风险。
在具体应用接口隔离原则时,应该根据以下几个规则衡量。
·接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
·为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
·了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同,要深入了解业务逻辑。
·提高内聚,减少对外交互。让接口用最少的方法完成最多的事情。
2.5.2 模拟场景
对于接口隔离的场景,在平时简单的业务开发中可能不会遇到,也可能体现得不明显。为了让大家更好地理解,举一个《王者荣耀》中英雄技能的例子,如果由你来开发这样的功能,会怎样设计?
《王者荣耀》里有很多英雄,可以分为射手、战士、刺客等,每个英雄有三种技能。这些技能该如何定义,让每个英雄实现相应的技能效果呢?接下来就分别使用两种不同的方式实现,来体现设计原则的应用。
2.5.3 违背原则方案
首先定义一个技能接口,实现的英雄都需要实现这个接口,进而实现自己的技能。
这里提供了四个技能的接口,包括射箭、隐袭、沉默、眩晕,每个英雄都实现这个接口。接下来实现两个英雄:后羿和廉颇。当然,这里为了说明问题进行了简化,英雄技能只有三个,与真实游戏中有所差别。
1.英雄后裔
在英雄后羿的类中,实现了三个技能,最后一个眩晕的技能是不需要实现的。
2.英雄廉颇
在英雄廉颇的类中,同样只实现了三个技能,有一个射箭的技能没有实现。
综上,每个英雄的实现类里都有一个和自己无关的接口实现类,非常不符合设计模式,也不易于维护。因为不仅无法控制外部的调用,还需要维护对应的文档,来说明这个接口不需要实现。如果有更多这样的接口,就会变得非常麻烦。
2.5.4 接口隔离原则改善代码
按照接口隔离原则的约定,应该在确保合理的情况下,把接口细分。保证一个松散的结构,也就是把技能拆分出来,每个英雄都可以按需继承实现。
接下来分别定义四个技能接口,包括射箭(ISkillArchery)、隐身(ISkillInvisible)、沉默(ISkillSilent)、眩晕(ISkillVertigo),如下所示。
(1)ISkillArchery.
(2)ISkillInvisible.
(3)ISkillSilent.
(4)ISkillVertigo.
有了四个技能细分的接口,英雄的类就可以自由地组合了。
英雄后羿的实现。
英雄廉颇的实现。
现在可以看到这两个英雄的类都按需实现了自己需要的技能接口。这样的实现方式就可以避免一些本身不属于自己的技能还需要不断地用文档的方式进行维护,同时提高了代码的可靠性,在别人接手或者修改时,可以降低开发成本和维护风险。