可扩展性(加)
新的功能可以很容易加入到系统中去。
灵活性(改)
可以允许代码平稳地修改,不会波及其他模块。
可插入性(换)
可很容易地将一个类抽出去,同时将令一个有同样接口的类加入进来。
传统的复用
- 代码的剪贴复用
- 算法的复用
- 数据结构的复用
面向对象设计的复用
- 数据的抽象化和继承:概念和定义可以复用
- 数据的抽象化和封装:保持和促进可维护性
- 多态性:实现和应用可以复用
复用的原则
- “开-闭”原则(Open-Closed Principle,OCP)
- 里氏代换原则(Liskov Substitution Principle,LSP)
- 依赖倒转原则(Dependency Inversion Principle,DIP)
- 接口隔离原则(Interface Segregation Principle,ISP)
- 组合/聚合复用原则(Composition/Aggregration Principle,CARP)
- 迪米特法则(Law of Demeter,LoD)
对可维护性的支持
可扩展性:OCP,LSP,DIP,CARP
灵活性:OCP,LoD,ISP
可插入性:OCP,LSP,CARP,DIP
面向对象可复用设计的一块基石。
一个软件实体应该对扩展开放,对修改关闭。
策略模式,简单工厂模式,工厂方法模式,抽象工厂模式,建造模式,桥梁模式,门面模式,调停者模式,访问者模式,迭代器模式
接口是实现构建可插入性的关键。
- 单方法接口(函数式接口)
- 标识接口
- 常量接口(不建议使用)
抽象类仅提供一个类的部分实现,用于继承。
- 继承(具体类不是用来继承的)
- 抽象类应该拥有尽可能多的共同代码
- 抽象类应该拥有尽可能少的数据
任何基类可以出现的地方,子类一定也可以出现。
Java对里氏代换原则的支持
Java编译器对子类对父类覆写(Override)的检查。
策略模式,合成模式,代理模式,代理模式
要依赖于抽象,不要依赖于实现。另一种表述:针对接口编程,而不是针对实现编程。
里氏代换原则是其基础。
三种耦合关系
- 零耦合关系:两个类之间没有耦合关系。
- 具体耦合关系:两个具体的(可实例化的)类之间,有一个类拥有另一个类对象的直接引用产生。
- 抽象耦合关系:一个具体类和一个抽象类(接口)之间,使两个必须发生关系的类存在最大的灵活性。
工厂方法模式,模板方法模式,迭代器模式
- Java接口
- Java抽象类
联合使用Java接口和Java抽象类:Java抽象类做为接口的缺省实现(命名:Abstract+接口名)。
应当为客户端提供尽可能少的单独的接口,而不是提供大的总接口。从客户的角度讲:一个类对另一个类的依赖性应当是建立在最小的接口上的。
定制服务原则拒绝向客户提供不需要提供的行为,符合迪米特法则。
备忘录模式,迭代器模式
要尽量使用合成、聚合,而不是继承达到复用的目的。新的对象通过向已有对象的委派达到复用已有功能的目的。
合成和聚合的区别
用C程序员较易理解的语言来讲,合成是值的聚合,聚合则是引用的聚合。
- 合成/聚合复用:黑箱复用
- 继承复用:白箱复用
Java API中的反面例子
Stack设置为Vector的子类,Properties设置为Hashtable的子类。
一个软件实体应当尽可能少的与其他实体发生相互作用。又称为最少知识原则(Least Knowledge Principle,LKP),也就是说一个对象应当对其他对象有尽可能少的了解。
如果两个类不必彼此直接通信,那么这两个类就不应该发生直接的相互作用。如果其中一个类要调用另一个类的某一个方法的话,可以通过第三个者(朋友,抽象陌生人)转发这个调用。
门面模式,调停者模式
对对象之间的信息流量、流向以及信息的影响的控制。
广义迪米特法则在类的设计上的体现
- 考虑将一个类设置成不变类。如String,BigInteger,BigDecimal。
- 尽量降低一个类的访问权限。package-private:当前库访问,public:当前库及其他库。
- 谨慎使用Serializable
- 降低成员访问权限
- 取代C struct
广义迪米特法则在代码层次上的体现
- 限制局域变量的有效范围