什么是模式
- 每个模式描述了一个在我们周围不断重复发生的问题及该问题的解决方案
- “不需要重新发明轮子”
原则
- 可复用--目标
- 面向对象--方法
面向对象
- 机器--程序员--现实世界
- 底层思维:向下,如何把握机器底层从微观理解对象构造
- 语言构造 / 编译转换 / 内存模型 / 运行时机制
- 抽象思维:向上,如何将周围世界抽象为程序代码
- 面向对象 / 组件封装 / 设计模式 / 架构模式
- 三大机制
- 封装:隐藏内部实现
- 继承:复用现有代码
- 多态:改写对象行为
- 深刻把握面向对象机制带来的抽象意义,理解如何用这些机制来表达现实世界,掌握什么是“好的面向对象设计”
- 软件设计复杂性的根本原因:变化(客户需求,技术平台,开发团队,市场环境)
两种设计思路
- 分解:面向过程
- 抽象:面向对象
面向对象设计
- 隔离变化:宏观层面,减小变化带来的影响
- 各负其责:微观层面,强调各个类的“责任”,由于需求变化导致的新增类型不应影响原类型(多态)
对象是什么
- 语言层面:封装了代码和数据
- 规格层面:一系列可被使用的公共接口
- 概念层面:拥有某种责任的抽象
八大设计原则
- 依赖倒置:高层模块(稳定)不应依赖于低层模块(变化),二者都依赖于抽象(稳定);抽象(稳定)不应依赖于细节(变化)(如MainForm和line都依赖于shape)
- 开放封闭:对扩展开放,对更改封闭;类模块可扩展但不可修改(桌子厂刷涂料)
- 单一职责:一个类应仅有一个引起它变化的原因,变化的方向隐含着类的责任
- 李氏替换:子类必须能替换它们的基类(is-a),父类的方法子类都要能用,继承关系要与组合关系区分
- 接口隔离:接口小而完备,不要都public,子类protected,本类private
- 优先使用对象组合,而不是类继承:继承本质是类属(组合)关系,简单理解成父-子关系会导致错误使用(高耦合)
- 封装变化点:一侧变化,一侧稳定,不只是表面上的封装代码和数据
- 针对接口编程,而不是针对实现:不要将变量类型声明为具体类(业务类,string不算),而是声明为某个接口(如抽象类shape),实现高内聚,松耦合(举例:秦兵器、活字印刷)
23种设计模式分类
- 从目的看
- 创建型:应对需求变化为对象创建带来的冲击,在创建对象的同时隐藏创建逻辑
- 结构型:应对需求变化为对象结构带来的冲击,关注类和对象的组合
- 行为型:应对需求变化为多个类交互过程中责任划分带来的冲击,关注对象间的通信
- 从范围看
- 类模式:处理类与子类的静态关系,偏重继承方案
- 对象模式:处理对象间的动态关系,偏重组合方案(一个类中包含另一个类的对象或指针)
- 从封装变化角度分类(黑体是必用的,斜体是不常用的)
- 组件协作:Template, Strategy, Observer/Event
- 单一职责:Decorator, Bridge
- 对象创建:Factory, Abstract Factory, Prototype, Builder
- 对象性能:Singleton, Flyweight
- 接口隔离:Facede, Proxy, Adapter, Mediator
- 状态变化:Memento, State
- 数据结构:Composite, Iterator, Chain of Responsibility
- 行为变化:Command, Visitor
- 领域问题:Interpreter
重构获得模式
- 面向对象设计模式是“好的面向对象设计”,即可以满足“应对变化,提高复用”的设计
- 现代软件设计的特征是“需求频繁变化”,设计模式应“寻找变化点,然后在变化点处应用设计模式”(封装变化点)(能看出代码哪里是稳定的,哪里是变化的)“什么时候、地点应用设计模式”比“理解设计模式结构本身”更重要
- 设计模式应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用,没有一步到位的设计模式,敏捷软件开发提倡“Refactoring to Patterns”是目前公认的最好的使用设计模式的方法
重构关键技法
- 静态->动态
- 早绑定->晚绑定
- 继承->组合
- 编译时依赖->运行时依赖
- 紧耦合->松耦合
书籍
- 《设计模式--可复用面向对象软件的基础》
- 《重构——改善既有代码设计》
- 《重构与模式》