迪米特法则
迪米特法则(Law of Demeter 或 LoD)又叫做最少知识原则(Least Knowledge Principle或简写为LKP),就是说,一个对象应当对其他对象有尽可能少的了解。
在软件系统中,一个模块设计的好不好的最主要、最重要的标志,就是该模块在多大的程度上将自己的内部数据和其他与实现有关的细节隐藏起来。一个设计好的模块可以将它所有的实现细节隐藏起来,彻底地将提供给外界的API和自己的实现分割开来。这样一来,模块与模块之间就可以仅仅通过彼此的API相互通信,而不理会模块内部的工作细节。这一概念就是“信息的隐藏”,或者叫做“封装”,也就是大家熟悉的软件设计的基本教义之一。
信息的隐藏非常重要的原因在于,它可以使各个子系统之间脱藕,从而允许它们独立地被开发、优化、使用、阅读以及修改。
广义迪米特法则在类的设计上的体现:
1. 优先考虑将一个类设置成不变类
2. 尽量降低一个类的访问权限
对于顶级(top-level)的类来说,只有两个可能的访问性等级。Package-private和public . 一个类可以设置成为package-private的,就不应该把它设置成为public的。
3. 谨慎使用Serializable
如果一个类实现了Serializable接口的话,客户端就可以将这个类串行后再并行化。假如以后这个类一旦修改,客户端势必也将改动。所以能不用就不用。
4. 尽量降低成员的访问权限
开闭原则:
开闭原则讲的是:一个软件实体应当对扩展开放,对修改关闭。
抽象化是关键
在java语言里,可以给出一个或多个抽象java类或java接口,规定出所有的具体类必须提供的方法的特征作为系统设计的抽象层。这个抽象层预见了所有的可能扩展。这就使得系统的抽象层不需修改,从而满足了“开-闭”原则的第二条:对修改关闭。
“开-闭”原则如果从另外一个角度讲述,就是所谓的“对可变性的封装原则”,讲的是找到一个系统的可变因素,将之封装起来。
从“开—闭”原则中可以看出面向对象设计的重要原则是创建抽象化,并且从抽象化导出具体化。
Java语言中的接口
接口是对可插入性的保证。
接口常见的用法:
1. 单方法接口
顾名思义,一个单方法接口只含有一个方法。Java中Runnable接口要求实现一个run方法。
2. 标示接口
标示接口是没有任何方法和属性的接口。标示接口不对实现它的类有任何语义上的要求,它仅仅表明实现它的类属于一个特定的类型。比如java中的Serializable接口。标示接口通常使用在工具类中,但一般不推荐使用。
3. 常量接口
即用java接口来声明一些常量。不过不推荐使用这种做法。
抽象类
抽象类应当拥有尽可能少的数据
基于抽象类的模式和原则
1.针对抽象编程
应当针对抽象类编程,而不是针对具体子类编程。
2.正确使用继承
在java中,继承关系可以分两种:一种是对接口的实现,称作接口继承;另一种是类对类的继承,称作实现继承。第二种继承关系是很容易被滥用的一种复用工具。
只要有可能,尽量使用合成(Composition),而不要使用继承来达到目的。
3.模板方法模式
模板方法模式不仅仅使用抽象类和继承关系作为对模式所涉及的角色的抽象化,模板方法模式根本就是关于继承的模式。
4.什么时候才应当使用继承复用
继承代表“一般化/特殊化”关系。
5 .使用继承关系的条件:
(1.) 子类是超类的一个特殊种类,而不是超类的一个角色。也就是要区分“Has-A”与“Is-A”两种关系的不同。Has-A关系应当使用聚合关系描述,而只有Is-A关系才符合继承关系。
(2.) 永远不会出现需要将子类换成另一个类的子类的情况。
(3.) 子类具有扩展超类的责任,而不是具有置换掉(Override)或注销掉(Nullify)超类的责任。
(4.) 只有在分类学角度上有意义时,才可以使用继承,不要从工具类继承。
里氏代换原则(【LiskovSubstitution Principle】LSP):
里氏代换原则是继承复用的基石。只有当衍生类可以替换掉基类,软件单位的功能不会受到影响时,基类才能被真正被复用,而衍生类也才能够在基类的基础上增加新的行为。
里氏代换原则要求凡是基类型使用的地方,子类型一定适用,因此子类必须具备基类型的全部接口。
依赖倒转原则(【Dependence Inversion】DIP)
实现“开—闭”原则关键是抽象化,并且从抽象化导出具体化的实现。如果说“开—闭”原则是面向对象设计的目标,依赖倒转原则就是面向对象设计的主要机制。
依赖倒转原则讲的是:要依赖于抽象,不要依赖于具体。
三种耦合关系
1. 零耦合关系:如果两个类没有耦合关系,就称之为零耦合。
2. 具体耦合关系:具体性耦合发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成。
3. 抽象耦合关系:抽象耦合关系发生在一个具体类和一个抽象类(或者java接口)之间,使两个必须发生关系的类之间存有最大的灵活性。
依赖倒转原则要求客户端依赖于抽象耦合。
依赖倒转原则的表述是:
抽象不应当依赖于细节;细节应当依赖于抽象。
还有一种表述就是:
要针对接口编程,不要针对实现编程。
针对接口编程的意思就是说,应当使用java接口和抽象java类进行变量的类型声明、参量的类型声明、方法的返还类型声明,以及数据类型的转换等。
联合使用java接口和java抽象类 Abstract + 接口
接口隔离原则(【Interface Segregation Principle】 ISP)
讲的是:使用多个专门的接口比使用单一的接口要好。换言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小的接口上的。
角色的合理划分:
一个接口相当于剧本中的一种角色,而此角色在一个舞台上由哪一个演员来演则相当于接口的实现。因此,一个接口应当简单地代表一个角色,而不是多个角色。如果系统涉及到多个角色的话,那么每一个角色都应当由一个特定的接口代表。
准确而恰当地划分角色以及角色所对应的接口,是面向对象的设计的一个重要的组成部分。
合成/聚合复用原则(【Composite/Aggregate Reuse Principle】 或CARP)
合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。
该设计原则有另一个更简短的表述:要尽量使用合成/聚合,尽量不要使用继承。
区分“Has-A”与“Is-A”
“Is-A”意思是一个类是另一个类的“一种”。而“Has-A”表示一个类代表另一个类的角色。