1.6 接口设计
软件的未来其实在很大程度上要看软件接口的前景如何。我们知道,计算机世界里的“接口”一词具有两种众所周知的含义:其一是指软件本身的狭义“接口”,如各种软件开发API等;其二则是指人与软件之间的交互界面。
接口设计一般出现在软件开发的概要设计阶段,概要设计要根据需求划分模块,而模块之间的联系就是通过定义接口实现的。例如有模块A和模块B,两者互相不知道对方实现的细节,当模块A要用到模块B中的功能时,就要使用模块B提供的外部接口(接口可以理解为一些功能函数的原型,包括函数名、参数列表和返回值);同样,模块A内可以定义内部接口,供模块A内部的函数调用。当各个模块中的接口完全设计好并通过评审后,各个模块就可以进行独立的详细设计及编码了。下面将详细地讲解接口设计的六大原则内容。
1.6.1 单一职责原则
单一职责原则(Single Responsibility Principle),简称SRP。就一个类而言,应该仅有一个引起它变化的原因。要避免一个类实现多个功能,否则当发生更改时会影响其他功能而致使复用成为不可能。使用的时候应该根据实际业务情况而定。实际使用时,类很难做到职责单一,但是接口的职责应该尽量单一。
1.6.2 依赖倒置原则
依赖倒置原则(Dependence Inversion Principle,DIP)。程序设计应该依赖抽象接口,而不应该依赖具体实现,即为接口编程思想。接口是稳定的,实现是不稳定的,一旦接口确定,就不应该再进行修改了。根据接口的实现,可以根据具体问题和情况,采用不同的手段去实现。
依赖倒置原则的定义需注意以下3点。
(1)高层模块不应该依赖低层模块,两者都应该依赖其抽象。
(2)抽象不应该依赖细节。
(3)细节应该依赖抽象。
依赖的3种写法如下。
(1)构造函数传递依赖对象。
(2)Setter方法传递依赖对象。
(3)接口声明依赖对象。
最佳实践:
(1)每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备。
(2)任何类都不应该从具体类派生。
(3)尽量不要覆写基类的方法。
(4)结合里氏替换原则使用。
(5)变量的表面类型应尽量是接口或抽象类。
1.6.3 迪米特法则
迪米特法则(Law of Demeter,LOD)又称最少知识原则(Least Knowledge Principle,简称LKP)。它表示一个实体应当尽可能少地与其他实体之间发生相互作用。
低耦合要求:
(1)朋友类是一种出现在成员变量、方法的输入和输出参数中的类。方法体内部的类不属于朋友类。
(2)迪米特法则要求类“内敛”一点,尽量不要对外公布太多的public方法和非静态的public变量,多使用private、protected、package-private等访问权限。
(3)如果一个方法放在本类中,既不增加类之间的关系,也对本类不产生负面影响,就放置在本类中。
(4)谨慎使用Serializable。
1.6.4 里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)。子类对象能够替换其父类对象被调用,即在程序中,任何调用父类对象实现的功能都可以调用子类对象来替换。
所有引用父类的位置必须能透明地使用其子类的对象,里氏替换原则为确保子类良好地继承父类定义了以下规范。
(1)子类必须完全实现父类的方法。
(2)子类可以有自己的“个性”(属性和方法)。
(3)覆写或实现父类的方法时输出结果可以被缩小。
(4)覆盖或实现父类的方法时输入参数可以被放大。
提示:在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。
1.6.5 接口隔离原则
接口,这里是指用interface关键字定义的接口,使用多个隔离接口比使用单个接口要好。经常提到的降低耦合、降低依赖主要也是通过接口隔离原则来达到的。
接口隔离原则的定义需注意以下两点。
(1)客户端不应该依赖它不需要的接口。
(2)类之间的依赖关系应该建立在最小的接口上。
概括地说,即建立单一接口,不要建立臃肿、庞大的接口。通俗来讲,接口尽量细化,同时接口中的方法尽量少。
保障接口的纯洁性有以下4点需要注意。
(1)接口要尽量小。
(2)定制服务。
(3)接口要高内聚。
(4)接口的设计是有限度的。
最佳实践:
(1)一个接口只服务于一个子模块或业务逻辑。
(2)通过业务逻辑压缩接口中的public方法,尽量让接口简单、代码不繁杂。
(3)对于已经被污染的接口,应尽量去修改。若变更的风险较大,则采用适配器模式进行转化处理。
(4)了解环境,拒绝盲从。每个项目或产品都有特定的环境因素,设计时不要盲从或跟从别人的设计,而要根据业务逻辑进行较佳的接口设计。
1.6.6 开闭原则
开闭原则表示的是程序的设计应该不约束扩展(即扩展开放),但又不能修改已有功能(即修改关闭)。
软件实体包括以下几个部分。
(1)项目和软件产品中按照一定的逻辑规则划分的模块。
(2)抽象和类。
(3)方法。
变化的3种类型如下。
(1)逻辑变化。
(2)可见视图变化。
(3)子模块变化。