1. 课程简介
本周开始进入到《C++设计模式》课程中。
A. 课程目标
a. 理解松耦合设计思想
b. 掌握面向对象设计原则
c. 掌握重构技法改善设计
d. 掌握GOF核心设计模式
B. 什么是设计模式
设计模式描述了一个重复发生的问题及该问题解决方案的核心。其目的在于提高软件设计过程中代码的复用率,提高代码抵抗变化的能力。
本课程以面向对象为基础,主要学习掌握“好的面向对象设计”。
2. 面向对象设计原则
面向对象设计原则在很多时候比具体的设计模式更为重要,设计模式是这些设计原则的实现。
A. 依赖倒置原则(DIP)
- 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)。
- 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。
B. 开放封闭原则(OCP)
- 对扩展开放,对更改封闭。
- 类模块应该是可扩展的,但是不可修改。
C. 单一职责原则(SRP)
- 一个类应该仅有一个引起它变化的原因。
- 变化的方向隐含着类的责任。
D. Liskov 替换原则(LSP)
- 子类必须能够替换它们的基类(is-a)。
- 继承表达类型抽象
E. 接口隔离原则(ISP)
- 不应该强迫客户程序依赖它们不用的方法。
- 接口应小而完备。
F. 优先使用对象组合,而不是类继承
- 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。
- 继承在某种程度上破坏了封装性,子类父类耦合度高。
- 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。
G. 封装变化点
- 使用封装来创建对象之间的分界层,让设计者可以在分界层一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。
H. 针对接口编程,而不是针对实现编程
- 不将变量类型声明为某个特定的具体类,而是声明为某个接口。
- 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
- 减少系统中各部分的依赖关系,从而实现“高内聚,松耦合”的类型设计方案。
3. 从封装变化角度对模式分类
A. 组件协作
- Template Method
- Strategy
- Observer / Event
B. 单一职责
- Decorator
- Bridge
C. 对象创建
- Factory Method
- Abstract Factory
- Prototype
- Builder
D. 对象性能
- Singleton
- Flyweight
E. 接口隔离
- Facade
- Proxy
- Mediator
- Adapter
F. 状态变化
- Memento
- State
G. 数据结构
- Composite
- Iterator
- Chain of Resposibility
H. 行为变化
- Command
- Visitor
I. 领域问题
- Interpreter
4. 重构关键技法
- 静态
->
动态 - 早绑定
->
晚绑定 - 继承
->
组合 - 编译时依赖
->
运行时依赖 - 紧耦合
->
松耦合
5. “组件协作”模式
现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
A. 模板方法(Template Method)
-
动机
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
B. 策略模式(Strategy)
-
动机
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
如何在在运行时根据需要透明地理性对象的算法?将算法与对象本身解耦,从而避免上述问题?
C. 观察者模式(Observer)
-
动机
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用变身对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
6. “单一职责”模式
在软件组件的设计中,如果责任划分不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
A. 装饰模式(Decorator)
-
动机
在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?
B. 桥模式(Bridge)
-
动机
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个维度的变化。
如何应对这种“多维度的变化”?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?