概述
1、设计模式的分类
总体来说设计模式分为三大类: 1、创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 2、结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 3、行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 4、其实还有两类:并发型模式和线程池模式。
1、设计原则
1.1、依赖原则
1、高层模块不应该依赖底层实现 2、抽象不应该依赖具体实现,实现应该依赖具体实现
1.2、开放封闭
1、一个类应该对(组合、继承开发),应该对修改闭合
1.3、面向接口
1、不要将某个变量型设计成特定类,而是声明成接口。 2、客户端不需要知道具体类型,只需要知道具体接口
1.4、封装变化点
将稳定点和变化点分离,扩展修改变化点,让稳定点和变化点的实现分离
1.5、单一职责
一个类应该仅有一个引起变化的原因
1.6、里氏替换
子类必须能够替换掉他的父类型,主要出现在子类覆盖父类实现,原来使用父类型程序可能出现错误,覆盖了父类型方法却没有实现父类职责
1.7、接口隔离
不应该强迫客户依赖于他们不用的接口
1.8、组合优于继承
继承耦合性高,组合耦合性低
设计模式遵循以上原则衍生出来的
2、模板方法
2.1、定义
定义一个操作类的框架,而将一些步骤延迟到子类中,使得子类可以改变这个框架的具体实现,但是主体部分还是由框架限制
2.2、背景
某个品牌动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代 更新表演流程;
2.3、要点
最常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;反向控制流程的典型应用;父类 protected 保护子类需要复写的子流程;这样子类的子流程只能父类来调用
2.4、没使用设计模式实现
从代码中可以看出,用户只能调用Show方法,而且后续show1、show2、show3会越来越大
2.5、使用设计模式实现
遵循上面所说,父类控制主体结构,通过protected保护下面的方法,只有子类可以重写,用户不能调用,所以,后续扩展show1、show2、show3就是特别方便
3、观察者模式
3.1、定义
定义一组一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发送改变时候所依赖他的对象都得到通知和更新
3.2、背景
气象站发布数据给数据中,数据中心通过处理,将气象站的数据更新到多个终端设备上
3.3、要点
观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合; 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了; 观察者不要依赖通知顺序,目标对象也不知道通知顺序; 常用在基于事件的ui框架中,也是 MVC 的组成部分; 常用在分布式系统中、actor框架中;
3.4、没使用设计模式实现
从代码中可以看出,没添加一个终端设备,都会调用数据中心的接口,然后进行显示,显然与我们的初衷违背了,我们初衷是,气象中心将数据给到,数据中心进行计算,计算完之后下发给所有的终端,在这个过程中不关心当前有多少终端设备,也不关心终端设备收到这个数据做什么具体的定制化操作。
3.5、使用设计模式实现
通过一个抽象类,规定了所有终端设备必须实现这个类,每个子类可以根据自己需要做一些定制化的开发,数据中心通过遍历终端容器进行数据下发。
4、策略模式
4.1、定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用 它的客户程序而变化。 ——《设计模式》 GoF
4.2、背景
某商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格;
4.3、要点
策略模式提供了一系列可重用的算法,从而可以使得类型在运⾏时方便地根据需要在各个算法之间 进行切换; 策略模式消除了条件判断语句;也就是在解耦合
4.4、没使用设计模式实现
节日的增加会让这个函数类的if else越来越多
4.5、使用设计模式实现
4.6、继续优化
上面写法用户使用的时候,暴露了太多创建过程,这种写法只需要传入对应枚举,实现过程在类中
4.7、优化
使用模板的方式更简单
5、单例模式
保证一个类只有一个实例,并提供该类全局的访问点
5.1、版本一
版本一存在的问题: 1、没有考虑多线程情况下,会出现多次创建 2、内存泄漏,我们的析构函数在private里面不会被调用
5.2、版本二
版本二存在问题: 虽然表面上解决了多线程出现多次创建的问题,和内存泄漏的问题,但是实际上隐藏一个bug new会做三件事:1、分配内存,2、调用构造函数,3、返回指针,但是cpu在执行的时候可能会优化执行过程,导致做这三件事的顺序发生改变,所以就会存在一个cpu reorder操作。
5.3、版本三
使用内存屏障确实可以解决这个问题,但是实现起来太复杂 g++ Singleton.cpp -o singleton -std=c++11
5.4、版本四
c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束。
- 1. 利⽤静态局部变量特性,延迟加载;
- 2. 利⽤静态局部变量特性,系统⾃动回收内存,⾃动调⽤析构函数;
- 3. 静态局部变量初始化时,没有 new 操作带来的cpu指令reorder操作;
- 4. c++11 静态局部变量初始化时,具备线程安全
5.5、版本五
项目中可以将Singleton写成一个模板父类,需要实现单例类继承他。 1、为什么子类中要把父类写成他的友元,因为子类是在父类中的GetInstance中构建出来的,需要调用子类的构造函数,而子类的构造函数又是private,所以需要将父类写成子类的友元。
6、工厂方法
定义一个用于创建接口,让子类决定实例化哪一个类Factory Method使得一个类实例化延迟到子类
背景
实现一个导出数据的接口,让客户选择数据的导出方式;
要点
解决创建过程比较复杂,希望对外隐藏这些细节的场景; 比如连接池、线程池 隐藏对象真实类型; 对象创建会有很多参数来决定如何创建; 创建对象有复杂的依赖关系;
6.1、版本一
1、遵循设计模式原则,面向接口编程,将导出数据封装成一个接口类,其他的都继承他,通过if进行判断确定new哪个子类
6.2、版本二
优化了创建子类部分,实现了一个简单工厂模式
6.3、版本三
将子类创建细节全部隐藏起来,用户知道的越少越好。
6.4、抽象工厂
7、适配器
7.1、定义
适配器模式是,将一个类的接口转化成另外一个客户希望的接口,使得原本接口不兼容不能一起工作的类,能够一起工作,它包含了类适配器和对象适配器。
7.2、背景
在STL中就用到了适配器模式。STL实现了一种数据结构,称为双端队列(deque),支持前后两段的插入与删除。STL实现栈和队列时,没有从头开始定义它们,而是直接使用双端队列实现的。这里双端队列就扮演了适配器的角色。队列用到了它的后端插入,前端删除。而栈用到了它的后端插入,后端删除。假设栈和队列都是一种顺序容器,有两种操作:压入和弹出。
7.3、实现
8、建造者模式
8.1、定义
建造者模式是将一个复杂的对象构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
8.2、背景
建造小人,一共需建造6个部分,头部、身体、左右手、左右脚。与工厂模式不同,建造者模式是在导向者的控制下一步一步构造产品的。建造小人就是在控制下一步步构造出来的。创建者模式可以能更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。
8.3、实现
9、代理模式
9.1、定义
为其他对象提供一种代理以控制对这个对象的访问。有四种常用的情况:(1)远程代理,(2)虚代理,(3)保护代理,(4)智能引用,主要介绍虚代理和智能引用两种情况。
9.2、背景
考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象。这里就可以运用代理模式,在打开文档时,并不打开图形对象,而是打开图形对象的代理以替代真实的图形。待到真正需要打开图形时,仍由代理负责打开。
9.2、虚代理实现
9.3、智能引用
10、桥接模式
10.1、定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。如何运用桥接模式呢?可以将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的
10.2、实现
11、装饰器模式
11.1、定义
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。有时我们希望给某个对象而不是整个类添加一些功能。比如有一个手机,允许你为手机添加特性,比如增加挂件、屏幕贴膜等。一种灵活的设计方式是,将手机嵌入到另一对象中,由这个对象完成特性的添加,我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。
11.2、实现
12、职责链模式
12.1、定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其思想很简单,考虑员工要求加薪。公司的管理者一共有三级,总经理、总监、经理,如果一个员工要求加薪,应该向主管的经理申请,如果加薪的数量在经理的职权内,那么经理可以直接批准,否则将申请上交给总监。总监的处理方式也一样,总经理可以处理所有请求。这就是典型的职责链模式,请求的处理形成了一条链,直到有一个对象处理请求。