侯捷-C++设计模式 笔记(已更新设计模式2个)
一、 设计模式简介
1.课程目标
松耦合设计思想面向对象设计原则重构技法改善设计GOF核心设计模式
2.什么是设计模式:
每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。
3.从面向对象谈起:
底层思维:向下,如何把握机器底层从微观理解对象构造语言构造,编译转换,内容模型,运行时机制
抽象思维:向上,如何将我们周围世界抽象为程序代码面相对象组件封装设计模式架构模式
4.深入理解面相对象
向下:深入理解三大面相对象机制:封装,隐藏内部实现 继承,复用现有代码 多态,改写对象行为
向上:深刻把握面相对象机制所带来的抽象意义,理解如何使用这些机制来表达现实世界,掌握什么是“好的面相对象设计”
5.抽象思维的背景:软件设计固有的复杂性
建筑商从来不会去给一栋已建好的100层高的楼房底下再新修一个小地下室——这样做花费极大而且注定要失败。然而令人惊奇的是,软件系统的用户在要求做出类似改变时却不会仔细考虑,而且他们认为这只是需要简单编程的事。 ——Object-Oriented Analysis and Design with Applications
6.软件设计复杂的根本原因:变化
客户需求的变化 技术平台的变化 开发团队的变化 市场环境的变化
7.如何解决复杂性
分解 人们面对复杂性有一个常见的做法:即分而治之,将大的问题分解为多个小问题,将复杂的的问题分解为多个简单问题。
抽象 更高层次来讲,人们处理复杂性有一个通用的技术,即抽象。由于不能掌握全部的复杂对象,我们选择忽视它的非本质细节,而去处理泛化和理想化了的对象模型。
示例图形绘制代码,来对比结构化和面向对象编程的区别:点,线,矩形
虚析构函数:C++中的public继承方式:
8.软件设计的目标
什么是好的软件设计?软件设计的金科玉律:复用!
二、面向对象设计原则
1.为什么要使用面向对象设计
变化是复用的天敌!面相对象设计的最大优势在于:抵御变化!
2.重新认识面向对象
理解隔离变化 从宏观层面来看,面相对象的构建方式更能适应软件的变化,能将变化所带来的影响减少为最小。 各司其职 从微观层面来看,面相对象的方式更强调个各类的“责任” 由于需求变化导致的新增类型,不应该影响原有类型的实现——是所谓各负其责 对象是什么? 从语言层面来看,对象封装了代码数据 “这里不准确,应该是操作和数据” 从规格层面讲,对象是一系列可被使用的公共接口 从概念层面讲,对象是某种拥有责任的抽象
“设计模式是针对有稳定点和稳定点的情况。分离稳定和变化,把变化关闭到笼子里。对于完全变化和完全稳定都不需要设计模式。”
3.面相对象设计原则1
"设计原则是一把尺子,去评判设计时候合理。” 依赖倒置原则(DIP) 高层模块(稳定)不应该依赖于底层模块(变化),二者都应该依赖于抽象(稳定) "MainForm不应依赖于Line、Rect,而应该都依赖于Shape" 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)
4.面相对象设计原则2
开放封闭原则(OCP) 对扩展开放,对更改封闭 类模块应该是可扩展的,但是不可修改
5.面相对象设计原则3
单一职责原则(SRP) 一个类应该仅有一个引起它变换的原因。 变换的方向隐含着类的责任
6.面相对象设计原则4
Liskov替换原则(LSP) 子类必须能够替换它们的基类(IS-A) 继承表达类型抽象
7.面相对象设计原则5
接口隔离原则(ISP) 不应该强迫客户程序依赖它们不同的方法 接口应该小而完备
8.面相对象设计原则6
优先使用组合,而不是类继承 类继承通常为“白箱复用”,对象组合通常为“黑箱复用” 继承在某种程度上破坏了封装性,子类父类耦合度高 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。
9.面相对象设计原则7
封装变化点 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响, 从而实现层次间的松耦合
10.面相对象设计原则8
针对接口编程,而不是针对实现编程 不将变量类型声明为某个特定的具体类,而是声明为某个接口。 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。 减少系统中各部分的依赖关系,从而实现“高内聚、低耦合” 的类型设计方案。 “面相接口设计,产业强盛的标志,接口标准化” “如秦朝兵器都有统一的标准” “毕升的活字印刷术”
11.将设计原则提升为设计经验
设计习语 Design Idioms Design Idioms描述与特定编程语言相关的低层模式,技巧,惯用法 设计模式 Design Patterns Design Patterns主要描述的是“类与相互通信的对象之间的组织关系,包括它们的角色,职责,协作方式等方面。 架构模式 Architectural Patterns Architectural Patterns描述系统中与基本结构组织关系密切的高层模式,包括子系统划分,职责,以及如何组织它们之间关系的原则
三、模版模式
1.GOF-23模式分类
从目的来看:
创建型(Creational)模式:将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体实现引来的冲击
结构型(Structural)模式:通过类继承或者对象组合获得灵活的结构,从而应对需求变化为对象的结构带来的冲击
行为型(Behavioral)模式:通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击
从范围来看:
类模式处理类与子类的静态关系
对象模式处理对象间的动态关系
2.从封装的角度对模式分类
组件协作:
Template Method
Strategy
Observer/Event
单一职责
Decorator
Bridge
对象创建
Factory Method
Abstract Factory
Prototype
Builder
对象性能
Singleton
Flyweight
接口隔离:
Facade
Proxy
Mediator
Adapter
状态变化:
Memento
State
数据结构:
Composite
Iterator
Chain of
Resposibility
行为变化:
Command
Visitor
领域问题
Interpreter
3.重构获得模式 Refactoring to Patterns
面向对象设计模式是“好的面相对象设计”,所谓“好的面相对象设计”指是哪些可以满足“应对变化,提高复用的设计”
现代软件设计的特征是“需求的频繁变化”,设计模式的要点是“寻找变化点,让后在变化点处应用设计模式,从而更好的应对需求的变化”,“什么时候,什么地点应用设计模式,比理解设计模式结构本身”更为重要。
设计模式的应用不宜先入为主,一上来就是用设计模式,是对设计模式的最大误用。没有一步到位的设计模式,敏捷软件开发时间提倡的“Rectoring to Patterns”是目前普遍公认的最好的使用设计模式方法。
4.推荐书籍
5.重构关键技法
静态->动态
早绑定->晚绑定
继承->组合
编译时依赖->运行时依赖
紧耦合->松耦合
6."组件协作"模式:
现代软件专业分工之后的第一个结果是“框架与应用的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
典型的模式:Template Method;Strategy;Observer/Event;
7.Template Method 模式的动机(Motivation)
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
#pragma region 模版模式 class Library { public: virtual bool Step2() = 0; // 抽象步骤 virtual void Step4() = 0; // 抽象步骤 void Step1() {/*执行固定步骤*/ } void Step3() {/*执行固定步骤*/ } void Step5() {/*执行固定步骤*/ } void Run() { Step1(); if (Step2()) { Step3(); } for (int i = 0; i < 3; i++) Step4(); Step5(); } }; class Application : public Library { bool Step2() override { // 实现具体步骤2 return true; } void Step4() override { // 实现具体步骤4 } }; //将固定的框架步骤封装到基类库中,应用程序只需实现具体步骤,从而实现代码复用和灵活扩展。 int main() { Application app; app.Run(); return 0; } #pragma endregion 模版模式模版模式这种,框架的库程序调用应用程序的晚完成的程序,叫晚绑定,反之叫早绑定。
8.模式定义
定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override重写)该算法的某些特定步骤。
————《设计模式》 GoF
设计模式的核心是分离变化点,隔离变化点。把变化点像小兔子一样关起来。相反,需求所有的都变化或者都固定, 都不适合任何设计模式。
Windows API 实现窗口编程 和 MFC的实现是典型的,由早绑定编程变为晚绑定的变化过程。
9.结构
10.总结
Template Method 模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。他用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
处理可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐它们设置为protected方法。
四、策略模式
1.动机
在软件狗之间过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
如何在运行时根据需要透明底更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
面向对象中说的复用,是指二进制层面的,编译层面的复用,而不是源代码级别的复用
2.定义
定义一些列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
3.结构
4.总结
Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都是需要Strategy模式。
如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
