开-闭原则(OCP)
在设计一个模块时,应当使这个模块可以在不被修改的前提下被扩展。开闭原则从另外一个角度讲述,就是“对可变性的封装原则”,即找到一个系统的可变因素,将之封装起来。这意味着两点:
可变性应当被封装到一个对象里面。继承应当被看做封装变化的方法,而不是从一般对象生成特殊对象的方法。
一种可变性不应当与另一种可变性混合在一起,实践上,通常类图的继承结构不会超过两层。
“将条件转移语句改写成为多态性”,将一个进行多次条件转移的商业逻辑封装到不同的具体子类。
如果一个条件转移语句没有涉及重要的商务逻辑,或者不会随着时间的变化而变化,也不意味着任何的可扩展性,那么它就没有涉及任何有意义的可变化。这就没必要转成多态性。
李氏代换原则(LSP)
一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别。
依赖倒转原则(DIP)
两种表述
Ø 抽象不应当依赖于细节,细节应当依赖于抽象。
Ø 要针对接口编程,不要针对实现编程。
要依赖于抽象,不要依赖于具体。传统过程性系统设计方法倾向于使高层次的模块依赖于低层次的模块,抽象层次依赖于具体层次。倒转,就是要把这种错误依赖关系倒转过来。
抽象层次包含应用系统的商务逻辑和宏观的,对整个系统来说重要的战略性决定,是必然性的体现。
只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参数的类型声明、方法返回类型的声明、属性变量的类型声明等。
多个工厂模式,解决创建过程中的依赖倒转问题。
任何方法无法回避java语言要求的new关键字和直接调用具体类的构造子的做法。简单工厂模式将这个违反“开-闭”原则以及依赖倒转原则的做法封装到了一个类里面,而工厂模式将这个违反原则的做法推迟到了具体工厂角色。
缺省适配模式
抽象类具有提供缺省实现的优点,java接口具有其他优点,所以缺省适配模式联合使用两者。声明类型的工作仍然由java接口承担,同时给出java抽象类,为接口提供一个缺省实现。如果需要加一个方法,那只用向抽象类加一个具体实现就可以。javaAPI很多使用这种模式,命名规范为Abstract+接口名,如AbstractCollection。
有些具体类非常稳定,不会发生变化,这时不需要发明一个抽象类型。
接口隔离原则(ISP)
使用多个专门的接口,比使用单一接口要好。换言之,一个类对另一个类的依赖性,应当建立在最小的接口上。
准确而恰当地划分角色以及角色所对应的接口,是面向对象设计的一个重要组成部分。
组合/聚合复用原则(CARP)
要尽量使用关联,而不是继承。
合成是值的聚合,聚合是引用的聚合。
迪米特原则(LoD)
Ø 只与你直接的朋友们通信
Ø 不要跟陌生人说话
Ø 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
朋友的条件
l 当前对象本身(this)
l 以参量形式传入到当前对象方法中的对象
l 当前对象的实例变量直接引用的对象
l 当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友
l 当前对象所创建的对象
缺点:遵循类之间的迪米特法则,会让系统的局部设计简化,每个局部都不会和远距离的对象有直接的关联,但会造成系统的不同模块间的通信效率降低,使系统的不同模块间不容易协调。
为了克服狭义的迪米特法则的缺点,可以使用依赖倒转原则,引入一个抽象的类型引用“抽象陌生人”对象,使“某人”依赖于“抽象陌生人’。换言之,就是将“抽象陌生人”变成朋友。
广义迪米特法则在类的设计上的体现
优先考虑将类设置成不变类,如String,BigInteger,BigDecimal
不变类的关键是,对于对象的所有操作都不可能改变原来的对象[只要需要,就返回一个改变了的新对象]。这就保证了对象不可改变。不可变类的实例是不能被修改的,每个实例中包含的信息都必须在该实例创建的时候就提供出来,并且在对象的整个生存周期内固定不变。非可变类确实有着自身的优势,如状态单一,对象简单,便于维护。其次,该类对象对象本质上是线程安全的,不要求同步。此外用户可以共享非可变对象,甚至可以共享它们的内部信息。(effective java 13条)
尽量降低一个类的访问权限
谨慎使用Serializable,软件提供商一旦将一个类设置成Serializable,就不能再在新版本中修改这个类的内部结构,包括private方法和句段。
尽量降低成员的访问权限
取代C Struct,提供set和get方法。
广义迪米特法则在代码上的体现
限制局域变量的有效范围