23种设计模式

  本来是想每一种设计模式写一篇随笔,但后来发现了别人的博客已经很完整、详细地做完这种事了。

  从效率的角度来考虑,本人就不重复造轮子了,本随笔只总结了每种设计模式的使用目的,具体实现方法和例子可以去以下链接看。

  别人的博客:https://blog.csdn.net/liang19890820/article/category/6783147

  介绍这些设计模式的视频:https://www.bilibili.com/video/av22292899

  PS:在23种模式全部写完之前都是未完待续!

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)独立变化的维度,且这多个维度都需要独立进行扩展,则可以使用桥模式。

  这个说明可能会有点玄,可以去别人的博客那里看看,他举了个电器与开关的例子,生动地介绍了桥模式。

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。

  适配器分为两种:类适配器和对象适配器。由于类适配器使用的是多继承,不推荐使用,因为有很多问题,视频中有提及。

15. 中介者(Mediator)

  当我们有很多类关系很密切,这些类之间经常互相调用,构成了一幅很复杂的关系图时,其中的某个类要进行变化都会出现牵一发动全身的危险情况,要考虑的情况会有很多,少考虑一种情况都可能会出现Bug。

  此时,我们可以用中介者模式:把这些类与某个特殊的类进行相互联系,原本的类之间不再有直接联系,原本的类想相互沟通时,将经过一个特殊的类。这个中介者相当于这些类之间的过渡器。

16. 状态模式(State)

  这个与策略模式差不多。

  State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态之间的解耦。

17. 备忘录(Memento)

  当我们想保存对象A的某些状态,一段时间后,想把修改后的对象A恢复回以前保存过的状态时,可以用备忘录模式。

  引用《设计模式》里的定义:

  在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。

18. 组合模式(Composite)

  如果某个对象内部有复杂的结构,而客户代码又过多地依赖此对象里面的结构,那么该对象内部的结构稍有更改,客户代码也要进行更改,造成维护性、扩展性的弊端。

  此时可以使用组合模式,它使用树状结构,通过递归的手段,把整个结构梳理,用户代码可以轻松调用,且今后不需更改。

  引用《设计模式》里的定义:

  将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

 19. 迭代器(Iterator)

  对于C++而言,面向对象的迭代器已经过时了,因为其需要频繁地使用虚函数,而虚函数是有成本的,导致效率较低。

  当今时代,泛型编程的迭代器更为流行,它是编译时的多态,性能更好。

20. 职责链(Chain of Resposibility)

  现在时代下,职责链用的频率不高,有点过时。

  引用《设计模式》里的定义:

  使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。

  将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

21. 命令模式(Command)

  对于C++而言,由于C++有函数对象,而函数对象与命令模式类似,且效率比命令模式高,故命令模式使用频率较低。

  对于其他语言,如java,C#等,命令模式使用较为广泛。

  引用《设计模式》里的定义:

  将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;

  对请求排队或记录请求日志,以及支持可撤销的操作。

22. 访问器(Visitor)

  引用《设计模式》里的定义:

  表示一个作用于其对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)

  简单来讲就是,假设一个对象拥有若干个元素(方法),如果每个访问者对此对象访问时,这些元素的操作都不相同,则可以使用访问器模式来处理。

  但是一旦这个对象要添加或减少某个元素(变化),则很多地方也随之进行修改(变化),这违背了开发封闭原则。

  故要使用访问器模式的前提是这个对象的元素是稳定的,以后都不需要更改的。但一般情况下,是很难做到的。

  访问器这一局限性导致其使用率低下。

23. 解析器(Interpreter)

  引用《设计模式》里的定义:

  给定一个语言,定义它的文法的一种表示,并定义一种解析器,这个解释器使用该表示来解释语言中的句子。

  适合使用解析器的情况:业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题。

  这个与字符串算法—正则表达式类似,也是把规律提取出来,变成表达式。不同点在于,面向对象的解析器使用了虚函数,表达式算法可以经常变化。

  但是,对于复杂的规则,解析器就不适用了,会产生类结构复杂、难以调试等问题,这时候需要求助于语法分析生成器这样的标准工具。

原文地址:https://www.cnblogs.com/mcomco/p/10779190.html