zoukankan      html  css  js  c++  java
  • 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)

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

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

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

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

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

  • 相关阅读:
    1063. Set Similarity
    A1047. Student List for Course
    A1039. Course List for Student
    最大公约数、素数、分数运算、超长整数计算总结
    A1024. Palindromic Number
    A1023. Have Fun with Numbers
    A1059. Prime Factors
    A1096. Consecutive Factors
    A1078. Hashing
    A1015. Reversible Primes
  • 原文地址:https://www.cnblogs.com/mcomco/p/10779190.html
Copyright © 2011-2022 走看看