单一责任原则
一个类应该只承担一个责任,即这个类只需要面对其所承担责任的需求变化
将一个类所承担的多个责任分解到若干个类中。
开-闭原则 OCP
一个软件实体应当对扩展开放,对修改关闭。
实现该原则的关键技术:
– 抽象化
• 从抽象层导出一个或多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的
• 抽象层预见了所有的可能扩展,因此,在任何扩展情况下都不会改变,这就是对改变的关闭
里氏代换原则 LSP
定义
– 如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都代换成O2时,程序P的行为没有变化,那么类型T2就是T1的子类型
– 反过来的代换是不成立的
– 是继承复用的基石
• 只有当子类可以替换掉基类,软件单位的功能不受影响的时候,我们才说基类真正被复用了
解决不满足LSP原则的办法:
– 创建一个新的抽象类,强调所有子类所能做的共同行为
– 将继承关系改写为聚集关系
依赖倒转原则 DIP
传统的过程性系统的设计方法倾向于使高层次的模块依赖于低层次的模块:抽象层依赖于具体层次
将传统的过程性系统的设计倒转:
– 抽象层次含有宏观的和重要的商务逻辑
• 应该由抽象层决定具体层次的实现和具体算法的改变
– 抽象层次含有战略的决策
• 应该由抽象层决定具体层次的战术
– 抽象层次含有必然性的选择
• 应该由抽象层来指导具体层次的偶然性选择
定义:
– 抽象不应当依赖于细节
– 要针对接口编程,不要针对实现编程
• 具体措施:
– 使用接口或者抽象类型进行变量的类型声明、参量的类型声明、方法的返还类型声明以及数据类型的转换
– 具体的类应当只实现接口或者抽象类中声明过的方法,而不应当给出多余的方法
抽象类和接口
– 抽象类可以提供某些方法的部分实现
– 接口则不可以
– 一个类最多只能继承一个抽象类
– 一个类可以实现多个接口
– 接口是定义混合类型的理想工具
• 混合类型指一个类具有主要的类型之外还具有其它的次要类型
接口隔离原则 ISP
• 接口污染
– 过于臃肿的接口
– 每一个接口都代表一个角色,实现一个接口的对象,在它的整个生命周期中都扮演这个角色
– 将角色区分清楚就是系统设计的一个重要工作
– 不应当将几个不同的的角色都交给同一个接口,而应当交给不同的接口
• 定义
– 使用多个专门的接口比使用单一的总接口要好
定制服务的出现
– 如果客户端仅仅需要某一些方法的话,那么就应当向客户端提供这些需要的方法,而不要提供不需要的方法
– 接口隔离原则讲的就是为同一个角色提供宽、窄不同的一个角色提供宽、窄不同的接口,以对付不同的客户端
– 这样做的好处:
• 系统的可维护性量的减少
– 向客户端提供的public接口是一种承诺,一个public接口一旦提供,就很难撤回
合成/聚合复用原则
定义
– 在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的
– 要优先使用合成/聚合,然后再使用继承
合成和聚合的区别
– 合成(Composition)与聚合(Aggregation)都是关联(Association)的特殊种类
– 聚合用来表示“拥有”关系或者整体与部分的关系,是一种弱关联关系
– 合成用来表示一种强的多得“拥有”关系,在这个关系里,部分和整体的生命周期是一样的
复用的基本种类
– 合成/聚合复用
– 继承复用
合成/聚合复用
– 优势
• 新对象存取成分对象的唯一方法是通过成分对象的接口
• 黑箱复用,成份对象的内部细节是新对象所看不见的
• 支持包装
• 可以在运行时间内动态进行,新对象可以动态地引用与成份对象类型相同的对象
– 缺点
• 管理复杂
• 不支持多态
• 代码量大
继承复用
– 优势:
• 新的实现较为容易,因为超类的大部分功能可以通过继承关系自动进入子类
• 修改或扩展继承而来的实现较为容易
• 易于管理
• 代码量少
– 缺点
• 破坏包装,因为超类的实现细节暴露给了子类
• 如果超类的实现发生改变,那么子类的实现也不得不发生改变
• 从超类继承而来的实现是静态的,不可能在运行时间内发生改变
is-a has-a
is-a表示的是属于得关系。比如兔子属于一种动物(继承关系)。
has-a表示组合,包含关系。比如兔子包含有腿,头等组件;就不能说兔子腿是属于一种兔子(不能说是继承关系)
"is a"关系和"has a"关系
假设有两个类:Computer和Employee。明显地,这两个类之间不存在"is a"的关系,即Employee不是计算机,它们之间没有继承关系的必要。因此不可能产生代码重用性。但这两个类之间是"has a"关系,即是支持的关系。例如,Employee"has a"Computer。明显地是一种支持关系。这种支持关系落实到代码中,就是在Employee中创建Computer的对象,调用其方法,到达完成某种运算和操作的目的。
Employee和Manager 类存在的则是"is a"关系,即Manager是Employee。它们之间存在共性,或者共同的属性。Manager是Employee的具体化;Employee是Manager的概括和抽象。概括性和抽象性的类,如Employee,在继承中则定义为超类。具体或代表对象特性的类,如Manager,则定义为子类。如果这是一个用来计算雇员工资的程序,那么在超类Employee中,我们应当包括所有子类都应该具有的、与计算工资有关的数据,例如name、employeeID、jobTitle、seniority、baseSalary以及用来计算基本工资部分的方法,如baseSalary()等。在Manager这个子类中,我们不仅继承Employee的所有数据和方法,还增加针对Manager的新的数据,如是否董事会成员boardMember、职务补贴merit等,因为除基本工资的计算之外,这些都影响到具有经理职务雇员的收入。
对两个类之间"is a"或是"has a"关系的分析,有助于我们确定它们之间是否存在继承关系,避免设计上的错误,因而达到提高代码重用性的目的
迪米特法则 LOD
定义:一个对象应该对其他对象保持最少的了解。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方案:尽量降低类与类之间的耦合。
设计模式的分类
• 创建型
– 如何创建对象
– Factory Method、Singlton、Abstract Factory...
• 结构型
– 如何处理类和对象的组合
– Adapter、Bridge、Proxy...
• 行为型
– 描述类或对象如何交互,怎样分配职责
– Command、Iterator、Strategy...
创建型
动机:一个对象经常变动,于是我们将它封装起来,让它对外的接口比较稳定,这样其他依赖该对象的对象就不需要随着需求改变了。
目标:
对类的实例化过程抽象化,将对象的创建和使用分离,让用户在实用类的过程中无需关心对象的创建。
类的创建模式,继承,将类的创建延迟到子类。
对象的创建模式,把对象的创建过程动态的委派给另一个对象。
内容:
简单工厂模式Simply Factory
工厂方法模式 Factory Method
抽象工厂模式 Abstract Factory
原型模式 Prototype
单例模式 Singleton
生成器模式 Builder
简单工厂模式Simply Factory: