1. 模板方法(template method)
把主程序写到父类文件中,部分子程序写到子类文件中,达到了算法可以灵活变化,且本体不需要进行改变的效果。
算法的实现是由程序库开发人员写的,他可以保证算法的正确性。后续想用此算法的人,只需根据需求重写一下变化部分,即可轻松调用,代码的复用性和准确性得到了保证。
具体可以看这里:设计模式—模板方法(template method)
2. 策略模式(Strategy)
策略模式就是对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。
当项目有大量if else时,可以用策略模式,达到后续修改时,只需添加类文件,而不是修改源代码的效果。
具体可以看这里:设计模式—策略模式
3. 观察者模式(Observer)
引用《设计模式》里的定义:
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
需要时,调用某个通知用途的函数,然后该函数通知所有观察者。
与UE4的多播委托有点类似。
4. 装饰模式(Decorator)
引用《设计模式》里的定义:
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码,减少子类个数)。
举个简单例子:A有2个子类:B,C; B有2个子类:B1,B2; C有3个子类:C1,C2,C3。
使用了装饰模式后,B1可以与C1,C2,C3任意组合;B2可以与C1,C2,C3任意组合。
如果不使用此模式,要达成上述效果,则需要在B的子类中添加大量子类,且有大量重复代码。
注意:装饰模式有一独特的特点:C的子类既继承A,又组合A。
这个模式可以明显体验出单一职责原则。
5. 桥模式(Bridge)
引用《设计模式》里的定义:
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
如果一个系统存在多个(≥ 2)独立变化的维度,且这多个维度都需要独立进行扩展,则可以使用桥模式。
A有2个子类:A1,A2; B有2个子类:B1,B2。B有2个虚函数,分别为FB1()、FB2();
A有个虚函数FA(),A1实现FA()调用B的FB1();A2实现FA()调用B的FB2();B1、B2根据需要实现FB1()、FB2()。
根据需要分别组合A的子类和B的子类,达到灵活多变的效果。
其中A与B形成桥梁。
6. 工厂方法(Factory Method)
引用《设计模式》里的定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦;手段:虚函数)到子类。
由于简单工厂模式中需要用到switch或者if else来判断实例化哪一个类,但这违背了开放封闭原则。因为每当我们新增一个类,都要去修改该工厂模式(新增类和在switch中添加代码)。
而本模式中,每个子工厂对应一个产品,调用时,只需自行判断调用哪个子工厂。新增一个类时,只需新增类和其对应的工厂,不需更改代码。(扩展,而不是修改)
7. 抽象工厂(Abstract Factory)
引用《设计模式》里的定义:
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
这个是工厂方法的延伸,或者说工厂方法是抽象工厂的一个特例。
简单来讲就是,工厂方法只生产一个对象,而抽象工厂则会生产一系列产品,用法大致相同。
但是,如果在这一系列产品中,要新增一个产品,则需要在每个工厂中新增一个产品,这违背了开放封闭原则!
所以,如果这一系列产品不是稳定的话,不要使用这种模式!
8. 原型模式(Prototype)
引用《设计模式》里的定义:
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。
通过克隆自己来创建对象。
由于这个是深拷贝,实现时可能需要复杂的代码。
9. 构建器(Builder)
引用《设计模式》里的定义:
将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。
说白了就是把构建部分独立出来,方便扩展和使用。
10. 单件模式(Singleton)
引用《设计模式》里的定义:
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
很简单,就是创建前检查这个类是否存在,如果是,则直接返回此类;如果否,则创建一个,并返回它。
如果是在多线程的环境下,则需要注意线程安全(double-checked locking)。
11. 享元模式(Flyweight)
引用《设计模式》里的定义:
运用共享技术有效地支持大量细粒度的对象。
此模式采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
创建一个类作为池来进行管理;想获取此池内某个对象时,先进行寻找,如果存在,则直接返回该对象;否则创建这个对象,加进池里,并返回它。
12. 门面模式(Facade)
引用《设计模式》里的定义:
为子系统中的一组接口提供一个一致(稳定)的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用(复用)。
一张图可以很好地解释:
说白了就是用一个工具人负责做联系,其它类只管调它的接口。它接口实现只管调用目标类的函数。
13. 代理模式(Proxy)
引用《设计模式》里的定义:
为其它对象提供一种代理以控制(隔离,使用接口)对这个对象的访问。
简单来讲就是,如果要调用类A里面的函数,我们可以设计一个类B,它可以调用类A的函数。然后,我们调用类B,起到了调用类A的函数,却不是直接调用类A的效果。
14.适配器(Adapter)
当我们想使用旧的一些接口A,但又不想修改它时(修改源代码,特别是很久远之前的代码很可能会出现各种bug。),我们可以使用适配器模式来创造兼用接口A的新接口B,然后调用B。
适配器分为两种:类适配器和对象适配器。由于类适配器使用的是多继承,不推荐使用,因为有很多问题,例如缺乏灵活性。
有一个老类A,它含有两个函数 a1(),a2()。有一群旧类(A1,A2,A3,A4...)继承了A,并且实现了各种各样的a1(),a2()。
创建新类B,它含有函数b1()。
创建B的子类B1,实现b1()。B1中有一个指针A*,类型为A。b1()实现为调用A的a1(),a2()。(根据需求,可能通过各种调用a1(),a2()来达到目标效果)
使用时,实例化B1,并把A*指定为某个旧类(A1,A2,A3,A4...),然后调用B1的b1()。(达到目的:想使用旧的一些接口)。
15. 中介者(Mediator)
当我们有很多类关系很密切,这些类之间经常互相调用,构成了一幅很复杂的关系图时,其中的某个类要进行变化都会出现牵一发动全身的危险情况,要考虑的情况会有很多,少考虑一种情况都可能会出现Bug。
此时,我们可以用中介者模式:把这些类与某个特殊的类进行相互联系,原本的类之间不再有直接联系,原本的类想相互沟通时,将经过一个特殊的类。这个中介者相当于这些类之间的过渡器。
类A,B,C,D相互间要进行交流,不要让它们直接联系,而是通过工具人来联系。
如果逻辑逐渐变得复杂,可以把工具人细分化各种细分工具人。
16. 状态模式(State)
这个与策略模式差不多。
State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态之间的解耦。
例如:
类State有4个函数:A(),B(),C(),D();
State有4个子类(状态):State1,State2,State3,State4;它们分别实现了在自身状态下的A(),B(),C(),D()
使用时,先设置当前状态,然后使用该状态的A(),B(),C(),D()。
State = State1;
State.A();
State.B();
State.C();
State.D();
17. 备忘录(Memento)
当我们想保存对象A的某些状态,一段时间后,想把修改后的对象A恢复回以前保存过的状态时,可以用备忘录模式。
引用《设计模式》里的定义:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
类A里面有个int变量;
类B里面有A的引用。
改变A的int变量后,在B里把A复制一份,从而改变B里面的A。(存储)
再改变A的int变量;
需要还原时,把B的A拿出来,复制给还原目标。
18. 组合模式(Composite)
如果某个对象内部有复杂的结构,而客户代码又过多地依赖此对象里面的结构,那么该对象内部的结构稍有更改,客户代码也要进行更改,造成维护性、扩展性的弊端。
此时可以使用组合模式,它使用树状结构,通过递归的手段,把整个结构梳理,用户代码可以轻松调用,且今后不需更改。
引用《设计模式》里的定义:
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
19. 迭代器(Iterator)
对于C++而言,面向对象的迭代器已经过时了,因为其需要频繁地使用虚函数,而虚函数是有成本的,导致效率较低。
当今时代,泛型编程的迭代器更为流行,它是编译时的多态,性能更好。
20. 职责链(Chain of Resposibility)
现在时代下,职责链用的频率不高,有点过时。
引用《设计模式》里的定义:
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
21. 命令模式(Command)
对于C++而言,由于C++有函数对象,而函数对象与命令模式类似,且效率比命令模式高,故命令模式使用频率较低。
对于其他语言,如java,C#等,命令模式使用较为广泛。
引用《设计模式》里的定义:
将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;
对请求排队或记录请求日志,以及支持可撤销的操作。
22. 访问器(Visitor)
引用《设计模式》里的定义:
表示一个作用于其对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。
简单来讲就是,假设一个对象拥有若干个元素(方法),如果每个访问者对此对象访问时,这些元素的操作都不相同,则可以使用访问器模式来处理。
但是一旦这个对象要添加或减少某个元素(变化),则很多地方也随之进行修改(变化),这违背了开发封闭原则。
故要使用访问器模式的前提是这个对象的元素是稳定的,以后都不需要更改的。但一般情况下,是很难做到的。
访问器这一局限性导致其使用率低下。
23. 解析器(Interpreter)
引用《设计模式》里的定义:
给定一个语言,定义它的文法的一种表示,并定义一种解析器,这个解释器使用该表示来解释语言中的句子。
适合使用解析器的情况:业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题。
这个与字符串算法—正则表达式类似,也是把规律提取出来,变成表达式。不同点在于,面向对象的解析器使用了虚函数,表达式算法可以经常变化。
但是,对于复杂的规则,解析器就不适用了,会产生类结构复杂、难以调试等问题,这时候需要求助于语法分析生成器这样的标准工具。