设计模式的原则才是模式的原力,所有的模式都是通过原则推导出来的
为什么用OOP
- 为了抵御变化
- 将变化的范围降低到最小
重新认识面向对象
- 隔离变化(宏观)
- 将变化所带来的影响减为最小
- 各司其职(微观)
- 新增类型不应该影响原来类型的实现
- 强调各个类的“责任”(功能)
- 比如
- 结构化设计中画画的责任是由mainForm来完成的
- 抽象化设计中画画的责任是由各个对象自己完成的
- 多态机制:接口一致但实现不同
- 对象是什么
- 封装了代码和数据
- 对象是一系列可被使用的公共接口
- 对象是某种拥有责任的抽象
设计原则
- 原则比模式更重要
- 通过原则实现模式
- 两段非常像的代码可能表达非常不同的模式
- 两段差异大的代码可能表达相同的模式
- 评判的标准是设计原则而不是公式
- 模式是由原则推导出来的
依赖倒置原则(DIP)
- 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)
- 隔离了变化
- MainForm不依赖Line、Rect,而依赖一个不变的Shape
- 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)
- 不要在抽象中使用子类
- 结构化设计
- MainForm依赖于Line和Rect
对象之间最弱的一种关联方式,是临时性的关联 代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系
- 抽象化设计
- 创建了Shape抽象类
开放封闭原则(OCP)
- 对扩展开放,对更改封闭
- 类模块应该是可扩展的,但是不可修改
- 结构化设计
- 每新增加一个形状,MainForm类就要修改
- [修改] 增加MainForm中保存这类图形的vector
- [修改] 在OnMouseUp抬起鼠标函数中,增加把这种图形加入vector的代码
- [修改] 在OnPaint绘制函数中,增加遍历这类图形vector并绘制的代码
- 每次修改需要重新编译这块代码
- 每新增加一个形状,MainForm类就要修改
- 抽象化设计
- [增加/扩展] 一个新的图形类,这时增加到另外一个文件中
- MainForm不需要修改类的声明
- MainForm::OnMouseUp不需要修改
- MainForm::OnPaint不需要修改
- 结构化设计
- 当新增需求的时候,只要增加一个文件,原来的文件都不用修改
单一职责原则(SRP)
- 一个类应该仅有一个引起它变化的原因
- 变化的方向隐含着类的责任
- 一个类含有7、80个方法
- 这个类含了太多功能
- 多个职责把类向多个方向拉扯
- 容易出问题
Liskov 替换原则(LSP)
- 子类必须能够替换它们的基类(IS-A)
- 所有需要父类的地方,子类的对象都可以传过去使用(都可以用子类替换)
- 继承表达类型抽象
接口隔离原则(ISP)
- 不应该强迫客户程序依赖它们不用的方法
- 接口应该小而完备
- 一旦客户对类的接口有依赖
- 类的这个接口必须保持不变
- 尽量少的暴露接口个外界
优先使用对象组合,而不是类继承
- 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
- 继承应该理解成类属关系
- SUV继承汽车
- 小轿车继承汽车
- 人继承动物
- 动物继承生物
- 继承应该理解成类属关系
- 继承在某种程度上破坏了封装性,子类父类耦合度高
- 父类给子类暴露太多东西
- 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低
封装变化点
- 使用封装来创建对象之间的分界层
- 让设计者可以在分界层的一侧进行修改
- 不会对另一侧产生不良的影响
- 从而实现层次间的松耦合
- 封装
- 底层思维
- 封装代码和数据
- 高层次理解
- 封装变化点
- 一侧变化
- 一侧稳定
- 底层思维
针对接口编程,而不是针对实现编程
- 不将变量类型声明为某个特定的具体类,而是声明为某个接口
- 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
-
减少系统中各部分的依赖关系,从而实现“高内聚、松耦合” 的类型设计方案
- 结构化设计
- MainForm依赖了Line、Rect等具体类型
- 抽象化设计
- MainForm依赖抽象接口
Shape
- MainForm::OnPaint执行Shape的绘制方法
- MainForm依赖抽象接口
- 接口标准化的含义
- 分工协作
- 雕版印刷
- 活字印刷
将8个设计原则提升为设计经验
- 设计习语 Design Idioms
- Design Idioms 描述与特定编程语言相关的低层模式,技巧,惯用法。
- 设计模式 Design Patterns
- Design Patterns主要描述的是类与相互通信的对象之间的组织关系
- 包括它们的角色、职责、协作方式等方面。
- 架构模式 Architectural Patterns
- Architectural Patterns描述系统中与基本结构组织关系密切的高层模式
- 包括子系统划分,职责,以及如何组织它们之间关系的规则。
模式分类
重构获得模式 Refactoring to Patterns
- 好的面向对象设计
- 应对变化,提高复用
- 设计模式的要点
- 寻找变化点
- 思维方式:把变化与稳定分开
- 代码不能一视同仁
- 软件系统有一些是变化的,有一些是不变的
- 在变化点处应用设计模式
- 更好地应对变化
- 什么时候、什么地点应用设计模式比理解设计模式结构本身更为重要
- 理解设计模式的代码执行流程只是肤浅的认识
- 真正是用设计原则重构代码
- 寻找变化点
- 设计模式的应用不宜先入为主
- 不是用设计模式去开发
- 而是在重构过程中推导出模式
- 敏捷软件开发实践提倡重构到模式(Refactoring to Patterns)是目前普遍公认的最好的使用设计模式的方法
- 先实现功能
- 再分析代码违背了什么设计原则
- 修正得到好的解决方案,碰巧是某种模式
重构关键技法
- 静态 -> 动态
- 早绑定 -> 晚绑定
- 继承 -> 组合
- 编译时依赖 -> 运行时依赖
- 紧耦合 -> 松耦合
“组件协作”模式
- 软件专业分工之后的第一个结果是
- 框架与应用程序的划分
- 我们开发程序,会使用到不同的有框架或库
- 这就涉及到我们的代码与既有代码之间的协作方式
- “组件协作”模式
- 通过晚期绑定(回调)
- 实现框架与应用程序之间的松耦合
- 是二者之间协作时常用的模式
- 典型模式
- Template Method
- Observer / Event
- Strategy