一文带你通俗理解23种软件设计模式(推荐收藏,适合小白学习,附带C++例程完整源码)

news2024/11/15 4:39:04

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

一、设计模式是什么?

       设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应用,不同的模式有各自的优缺点,开发者可以基于自身需求选择合适的设计模式,去解决相应的工程难题。

       良好的软件设计和架构,可以让代码具备良好的可读性、可维护性、可扩展性、可复用性,让整个系统具备较强的鲁棒性和性能,减少屎山代码出现的概率。

       想要熟练运用设计模式,提高自己的编程能力和架构能力,只有在自己工作中,结合自身工作内容,多思考多实践。本文只能通过举一些通俗的例子,来帮助你更快或者说更容易地去理解设计模式的一些底层逻辑,光看可不行哦,一定要多动手实操,看完本文就去实践吧。

二、六大设计原则

2.1 单一职责原则

       该原则:对一个类而言,其职责应该只有一个,即只有一个可以引起其改变的原因。

       若类承担的职责过多,其内部耦合性就大大增加,某个职责的变化会带给其他职责不可预估的影响,这样设计会使得类相对脆弱。

       通俗地理解就是,你如果在当保安,就不要老想着帮清洁打扫卫生,你的领导可能会因为你打扫卫生夸你,但更可能会因为你保安工作没做到位而骂你。

2.2 开闭原则

       该原则:面向对象编程领域中规定,软件中的对象应该对于扩展是开放的,对于修改是封闭的,即一个实体允许在不改变它原有源代码前提下变更其行为。

       开闭原则可以使我们的软件具备较优的鲁棒性,代码更模块化且利于扩展和维护,从而提高开发效率。经常开发的工程师都清楚,在一个旧的系统中添加新的功能是很不容易的,如果你添加功能要大量修改原有的代码内容,大概可能出现如下几类情况:

  1. 你能力特强,对原有系统非常了解,也可能本来就是你写的,你添加的功能不仅完成了业务需求,还没有给系统带来bug和崩溃。无非就是测试工程师可能要重新进行一整轮的整体测试工作,然后不太开心,也还好啦。
  2. 你修改或添加的内容让系统跑不起来了,那你就要重写你的代码或者研究如何修复你产生的bug,当然也可能会埋下未知的雷。大概率测试工程师会天天问候你。工作效率大大降低,这也是大多数人面临的情况,懂得都懂。
  3. 你修改或添加的内容完成了业务需求,也没有让系统崩溃,但你也不清楚会不会引起其他地方在某些情况下出现未知bug,测试工程师大概率也没测出来,毕竟常规情况下不会异常,但未来可能会爆雷,那就让未来的人自求多福吧。这类情况,即使你的代码没有问题,但会让系统更加复杂,更加不可读,更加无法扩展。

       综上来看,如果系统有良好的设计,也尽可能满足了开闭原则,那么后续在扩展新功能时,原有系统不会受到影响,只需要针对新扩展的部分重点测试即可(测试工程师狂喜),即便是扩展的功能出现了问题,也相对容易进行修复,最坏就是把扩展的内容全删掉罢了。

2.3 里氏代换原则

       该原则:任何基类可以出现的地方,子类一定可以出现。

       该原则是继承复用的基石,只有子类能完全替换基类,且功能不受影响,基类才算是真正被复用。想满足该原则,一般来说就尽量从抽象类继承,而不是从具体类继承,还可以根据如下4点来判断:

  1. 子类必须实现父类的抽象方法,但不能重写父类的非抽象方法(已实现)。你重写了那就没法替换了。
  2. 子类可以增加自己特有的方法。父类有的子类也有就行。
  3. 子类实现父类的方法,输入要比父类的输入更宽松。比如父类输入一个hashmap,子类就输入一个map,map是包含hashmap的,这样无论用子类父类调用该方法,只要输入hashmap,都用的父类方法。
  4. 子类实现父类的方法,输出要比父类的输出更严格。比如父类输出一个map,子类就输出一个hashmap,这样只要输出map,都用的父类方法,反过来编译器会报错。

       里氏代换原则可以很大程度降低代码出问题的概率,也是一个良好的设计和编程习惯。

2.4 依赖倒置原则

       该原则:程序要依赖于抽象接口,不要依赖于具体实现,对抽象编程,不要对实现编程,这样降低了客户端和实现模块间的耦合。一般情况抽象变化的概率小,依赖于抽象,即使实现细节一直变化,也不影响客户端。

       想遵循该原则,尽量满足如下4点要求:

  1. 每个类尽量有接口或抽象类,这样才能基于抽象编程。
  2. 变量的类型尽量是接口或抽象类。
  3. 尽量不要从具体类继承。
  4. 尽量不要覆盖基类或父类已实现的方法。

       依赖倒置原则的目的就是实现解耦,高层模块不再依赖于具体实现。

2.5 最小知识原则

       该原则:也称为迪米特原则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果需要调用另一个对象的某个方法,可以通过引入一个合理的第三者来调用,从而降低他们之间的耦合度。

       想遵循该原则,尽量满足如下3点要求:

  1. 尽可能创建松耦合的类,这样复用率高,当发生修改时,相关联的类也不会有太大的影响。
  2. 类的结构设计中,成员变量和成员方法的访问权限尽可能降低。
  3. 尽可能把类设计为不变类。

       最小知识原则尽可能地减少了对象间的联系,保持功能模块相对独立,降低依赖。

2.6 合成复用原则

       该原则:在一个新的对象中使用已有的对象,使之成为新对象的一部分,通过委派调用已有对象的方法达到复用目的,应优先采用合成/聚合的方式,其次采用继承方式。

       通过合成/聚合复用的优势:

  1. 维持了封装性。看不到已有对象的内部细节,属于黑箱复用。
  2. 耦合度低。所需依赖少,新对象只能通过成分对象的接口调用其方法。
  3. 复用的灵活性高。运行时动态进行复用,可引用与成分对象类型相同的对象。

       应用合成复用原则,不要放松对嵌入对象的管理,尤其是嵌入对象过多时,这很考验开发者的实力。

三、23种软件设计模式

       3.1-3.5是创建型模式,3.6-3.12是结构型模式,3.13-3.23是行为型模式。

3.1 单例模式

       单例模式是一种创建型的软件设计模式,在工程项目中非常常见。通过单例模式的设计,使得创建的类在当前进程中只有一个实例,并提供一个全局性的访问点,这样可以规避因频繁创建对象而导致的内存飙升情况。

       实现单例模式的三个要点:

1)私有化构造函数:这样外界就无法自由地创建类对象,进而阻止了多个实例的产生。

2)类定义中含有该类的唯一静态私有对象:静态变量存放在全局存储区,且是唯一的,供所有对象使用。

3)用公有的静态函数来获取该实例:提供了访问接口。

       单例模式一般分为懒汉式和饿汉式。

1)懒汉式:在使用类对象(单例实例)时才会去创建它,不然就懒得去搞。

2)饿汉式:单例实例在类装载时构建,有可能全局都没使用过,但它占用了空间,就像等着发救济粮的饿汉提前排好队等吃的一样。

       实现单例模式通常面临两个问题,线程安全内存泄漏。针对线程安全,可通过双重检测锁的方式来解决;针对内存泄漏,可通过资源管理的方式来解决,如智能指针静态嵌套类。

       具体代码实现详情见:

设计模式之单例模式(C++)_翟天保Steven的博客-CSDN博客

3.2 工厂模式

       工厂模式是一种创建型的软件设计模式。定义一个用于创建对象的工厂接口,并让工厂子类决定实例化哪一个产品类,使产品类的实例化延迟到工厂子类中执行。说白了就是用来造东西的,一般是比较简单的东西,我们不需要知道它如何生产的,直接从工厂拿到产品即可。

       工厂模式的优点:

  1. 良好的封装性。将产品的实例化封装执行,避免被修改,这样的产品具备良好的一致性。
  2. 良好的扩展性。增加产品时,同步增加一个工厂子类,不会违反开闭原则。
  3. 标准的解耦合框架。使用者只需要知道自己要什么产品即可,不用去管产品具体的特性等等,降低了模块间的耦合。

       工厂模式的缺点:

  1. 代码量大。每加一个产品,都要加一个工厂子类,代码会显得臃肿。
  2. 不利于扩展复杂的产品结构。如果你要苹果、香蕉、梨,工厂模式的结构还可以,但如果你要山东的苹果、海南的香蕉、北京的苹果,就显得结构呆呆的。这可以用抽象工厂模式解决,对产品族和产品种类进行区分。

       举例:水果工厂生产,具体代码实现详情见:

设计模式之工厂模式(C++)_翟天保Steven的博客-CSDN博客

3.3 抽象工厂模式

       抽象工厂模式是一种创建型的软件设计模式,该模式相当于升级版的工厂模式。

       如果说工厂模式对应一个产品系列,那抽象工厂就对应了多个产品系列。比如工厂模式中有鞋子、衣服和裤子可以生产,那抽象工厂模式就会衍生出耐克工厂和阿迪工厂,这两个工厂分别生产各自品牌的鞋子、衣服和裤子,客户只需要选择具体工厂和想要的产品即可。如果想要替换产品系列,也只需要将具体工厂切换为别的品牌就行了。

       抽象工厂模式的优点:

  1. 具体类分离。具体产品类在具体工厂的实现中进行了分离和归类。
  2. 易于更换产品族。当客户想要改变整个产品族时,只需要切换具体工厂即可。
  3. 利于产品一致性。当产品族的各个产品需要在一起执行时,抽象工厂可以确保客户只操作同系列产品,而不会进行跨品牌的组合。

      抽象工厂模式的缺点:

  1. 不利于添加新种类产品。每加一个新的种类,如多一个项链类型的产品,那每一个具体工厂都要进行代码的扩展,且破坏了原先规定的结构,违反了开闭原则。

      举例:不同国家水果工厂生产,具体代码实现详情见:

设计模式之抽象工厂模式(C++)_翟天保Steven的博客-CSDN博客

3.4 建造者模式

       建造者模式是一种创建型的软件设计模式,用于构造相对复杂的对象。

       建造者模式可以将复杂对象的构建与它的表示分离,使得相同的构建过程可以得到不同的表示。如果说工厂模式和抽象工厂模式更注重产品整体,那建造者模式则更在乎产品的组成和细节

       建造者模式的优点:

  1. 封装性好。有效地封装了建造过程(主要业务逻辑),使得系统整体的稳定性得到了一定保证。
  2. 解耦。产品本身和建造过程解耦,相同的建造过程可以创建出不同的产品。
  3. 产品建造过程精细化。该模式注重产品创建的整个过程,将复杂的步骤拆解得到多个相对简单的步骤,使得系统流程更清晰,且对细节的把控更精准。
  4. 易于扩展。如果有新产品需求,只需要添加一个建造者类即可,不需要改动之前的代码,符合开闭原则。

      建造者模式的缺点:

  1. 产品的组成部分和构建过程要一致,限制了产品的多样性
  2. 若产品内部有结构上的变化,则整个系统都要进行大改,增加了后期维护成本

      举例:生产电脑,具体代码实现详情见:

设计模式之建造者模式(C++)_翟天保Steven的博客-CSDN博客

3.5 原型模式

       原型模式是一种创建型的软件设计模式,通俗的来讲就是复制粘贴。

       通过一个原型对象,快速地创建出多个一致的对象,并对其进行相关的操作。比如文件夹中存放了一个Word文件,你把文件复制了一个副本出来,原件不动,对副本进行修改以达到自己的目的。原型像是一个模板,你可以基于它复制好多对象,而复制出来的副本产生任何变化都不会影响到原型(注意:前提是clone的实现要满足深拷贝)。

       原型模式的优点:

  1. 便捷、简洁、高效。不需要考虑对象的复杂程度,只需要复制即可。
  2. 无需初始化。可动态地获取当前原型的状态,并在当前基础上进行拷贝。
  3. 允许动态增加或减少产品类

      原型模式的缺点:

  1. 每个类都需要配备一个clone函数,若对已有的类进行改造,需要修改其源码,违背了开闭原则。

      举例:文件复制粘贴,具体代码实现详情见:

设计模式之原型模式(C++)_翟天保Steven的博客-CSDN博客

3.6 适配器模式

       适配器模式是一种结构型的软件设计模式,也称包装模式,即将相对复杂的功能(可能用到多个类)封装起来,提供一个使用者想要的接口,使用者只需要调用接口,不需要知道接口里封装的内容是如何实现的。

       个人工作中经常能用到适配器模式,比如在面对一些第三方库或者SDK开发时,它们的接口往往与我们自己想要的接口不一致,此时适配器模式可以很好地扮演一个接口转换器的角色,将别人的接口与我们的接口对应上。

       适配器模式的优点:

  1. 良好封装性。接口内的内容对使用者而言是透明的,即看不见,这确保了内部功能具备较好的封装性,不易被改动。
  2. 解耦。不匹配的两方在适配器的作用下可以做到解耦,不需要修改任何一方原有代码逻辑。
  3. 良好复用性。适配的两方不需要做任何修改,业务的实现可以通过适配器来完成,不同的业务可以使用不同的适配器。
  4. 良好扩展性。若要增加业务场景,只需要增加适配器类,来满足业务即可。

      适配器模式的缺点:

  1. 不利于维护。因为业务的实现基于适配器完成,适配器中代码的复杂程度会越来越高,不熟悉业务或者底层逻辑的人难以短时间内接手维护。
  2. 系统结构易混乱。当业务量快速增加时,适配器类的数量也会快速增加,没有良好的系统架构布局,最终会使得整个系统臃肿且危险。

      举例:调用第三方相机SDK,具体代码实现详情见:

设计模式之适配器模式(C++)_翟天保Steven的博客-CSDN博客

      类适配器模式:

       对象适配器模式:

3.7 桥接模式

       桥接模式是一种结构型的软件设计模式,将抽象部分与实现部分分离,使他们可以独立地变化。

       举例来说,黑色钢笔、红色油笔、红色钢笔等等,如果颜色和笔类型合起来考虑,那类的复杂度将难以想象,若有10个颜色,10个笔类型,那要有10*10个类来涵盖所有类型的笔。但是如果拆分颜色和笔类型,通过组合的形式获得目标,那只要10+10个类即可。这样设计极大降低了系统复杂度和耦合程度。

       桥接模式的优点:

  1. 扩展性好。抽象与实现分离,扩展起来更便捷,可以获得更多样式的目标。
  2. 解耦。不同抽象间的耦合程度低。
  3. 满足设计模式要求的合成复用原则和开闭原则
  4. 封装性好。具体实现细节对客户而言是透明不可见的。

      桥接模式的缺点:

  1. 使用场景有限制。只有系统有两个以上独立变化维度时才适用。

      举例:画笔绘画,具体代码实现详情见:

设计模式之桥接模式(C++)_翟天保Steven的博客-CSDN博客

3.8 组合模式

       组合模式是一种结构型的软件设计模式,将对象组合成树形结构,以凸显“部分-整体”的层次结构,使客户端对单个对象和组合对象的操作具备一致性。

       组合模式和桥接模式都应用了组合的思想,不同之处在于:桥接模式侧重于同级别间的组合,如多个属性的组合,避免了类爆炸;组合模式侧重于部分和整体的组合,避免了单对象和组合对象的区别对待,那样会增加程序复杂度。

       组合模式的优点:

  1. 层次鲜明。凸显“部分-整体”的层次结构。
  2. 一致性。对叶子对象(单)和容器对象(组合)的操作具备良好一致性。
  3. 节点自由度高。在结构中按需自由添加节点。

       组合模式的缺点:

  1. 设计更抽象
  2. 应用场景限制

       举例:文件系统,具体代码实现详情见:

设计模式之组合模式(C++)_翟天保Steven的博客-CSDN博客

       透明式组合模式:

       安全式组合模式:

3.9 装饰器模式

       装饰器模式是一种结构型的软件设计模式,在不改变原类文件或使用继承的前提下,动态地扩展一个对象,进而达到增强或者增加对象功能的目的。

       装饰器模式的优点:

  1. 灵活性好。相比较继承,装饰模式扩展对象功能更加灵活。
  2. 扩展性好。不同装饰组合,可以创造出各式各样的对象,且避免了类爆炸。
  3. 满足设计模式要求的开闭原则和合成复用原则
  4. 透明性好。客户端针对抽象操作,对具体实现的内容不可见。

       装饰器模式的缺点:

  1. 复杂性高。装饰模式的设计往往具备较高复杂度,对开发者的水平要求高。

       举例:炒菜加料,具体代码实现详情见:

设计模式之装饰器模式(C++)_翟天保Steven的博客-CSDN博客

3.10 门面模式

       门面模式是一种结构型的软件设计模式,也叫外观模式,它提供了统一的接口去访问多个子系统的接口。举个例子,一个餐馆里有许多角色,每个角色就是一个子系统,餐馆就是总系统,客人来餐馆只需要按要求点餐,不需要管餐馆是怎么运作的。

       门面模式的优点:

  1. 简洁易使用。为复杂的模块和系统提供了一个简单的接口,简易化操作。
  2. 保证了子系统独立性。子系统间独立性良好,彼此间一般不受影响,如何使用由门面决定。
  3. 保证了系统稳定性。当直接使用子系统,可能会出现无法预知的异常时,门面模式可通过高层接口规范子系统接口的调用,且有效阻隔子系统和客户端间的交互,进而增强系统鲁棒性。
  4. 隐秘性好。门面将子系统的具体细节都封装了起来。

       门面模式的缺点:

  1. 不符合开闭原则。添加新系统要对门面进行修改。
  2. 对开发者要求高。开发者需要了解子系统间的业务逻辑关系,这样才能确保封装的高层接口是有效且稳定的。

       举例:餐馆吃饭,具体代码实现详情见:

设计模式之门面模式(C++)_翟天保Steven的博客-CSDN博客

3.11 代理模式

       代理模式是一种结构型的软件设计模式,在不改变原代码前提下,提供一个代理,以控制对原对象的访问。

       代理模式的优点:

  1. 职责清晰。真实对象专注于自身业务逻辑,不用考虑其他非本职内容,交给代理完成。
  2. 高拓展性。真实对象的改变不影响代理。
  3. 解耦。将客户端与真实对象分离,降低系统耦合度。
  4. 提高性能。虚拟代理可以减少系统资源的消耗。
  5. 高安全性和稳定性。代理能很好地控制访问,提高程序安全。

      代理模式的缺点:

  1. 增加系统复杂度。代理的职责往往较冗杂。
  2. 请求速度降低。客户端与真实对象中加入代理,一定程度上会降低整个系统流程的运行效率。

       举例:游戏代理,具体代码实现详情见:

设计模式之代理模式(C++)_翟天保Steven的博客-CSDN博客

3.12 享元模式

       享元模式是一种结构型的软件设计模式,通过共享对象的方式,尽可能减少内存占用,从而达到优化的目的。

       就像打麻将,同时有10桌在玩,每桌都有4个"八筒",如果建立40个"八筒"对象,那就非常冗余,但如果用享元模式建立一套麻将牌,每桌打出"八筒"时,就调用享元中的"八筒",相当于只用了1个对象,这样即节省了资源,也完成了需求。

       上述例子中,桌号和牌号就是享元模式的外蕴状态,如A1八筒,就是A桌的第一个"八筒",A和1是外蕴状态;而卡牌"八筒"本身就是内蕴状态,内蕴是可以共享的。外蕴随环境变化,占用资源也少的多,往往只是简单的数据结构。

       享元模式的优点:

  1. 减少资源浪费。共享资源极大程度降低了系统的资源消耗。
  2. 提高系统运行效率。当资源过度使用时,系统效率会大受影响。

       享元模式的缺点:

  1. 维护共享对象,需要额外开销
  2. 系统复杂度提高。运行享元,除了内外状态,还有线程方面都要充分考虑。

       举例:模拟打牌,具体代码实现详情见:

设计模式之享元模式(C++)_翟天保Steven的博客-CSDN博客

3.13 策略模式

       策略模式是一种行为型的软件设计模式,针对某个行为,在不同的应用场景下,有不同的实现算法,并且可以互相替换。比如两军交战,军队会采用不同的阵法、策略、兵法应对不同的战况。

       策略模式的优点:

  1. 便于管理算法族。一套策略对应一套算法,该模式可以很好地进行维护和管理。
  2. 避免使用多重条件语句,如if else和switch。庞大的系统中,过多的多重条件语句会让代码显得臃肿和不易维护,提高出错概率。
  3. 封装算法。提高了算法的保密性和安全性。
  4. 符合开闭原则

      策略模式的缺点:

  1. 客户端需要了解每种策略,并自行决定何时何地使用何种策略
  2. 随着策略增多,策略类数量增加,要注意对类的维护

       举例:军队策略,具体代码实现详情见:

设计模式之策略模式(C++)_翟天保Steven的博客-CSDN博客

3.14 模板模式

       模板模式是一种行为型的软件设计模式,在父类中定义了一个模板算法,只实现模板中的公共部分,将可变部分放在子类中实现,不同的子类对同一模板有不同的扩展和实现。

       模板模式的优点:

  1. 良好复用性。父类中公共部分可以多次使用,具备好的环境适应性。
  2. 良好扩展性。子类对父类模板的具体实现作扩展。
  3. 符合开闭原则。基于模板扩展功能,不需要改动原有代码。

      模板模式的缺点:

  1. 类个数增加。基于模板的每个实现,都要定义一个子类,容易使代码量膨胀。
  2. 若父类模板有改动,则子类均要同步更改

       举例:安装电脑,具体代码实现详情见:

设计模式之模板模式(C++)_翟天保Steven的博客-CSDN博客

3.15 命令模式

       命令模式是一种行为型的软件设计模式,行为请求者通过发起命令,使得行为实现者执行命令要求的行为。通常行为请求者和行为实现者之间是强耦合的,而命令模式能很好地将其解耦。

       命令模式的优点:

  1. 解耦。行为请求者和实现者之间的强耦合关系解除。
  2. 良好扩展性。扩展命令只需要添加一个子类即可,满足开闭原则。
  3. 有效管理命令。如记录、撤销、组合等。

       命令模式的缺点:

  1. 命令增加,子类数量也增多

       举例:军队指挥,具体代码实现详情见:

设计模式之命令模式(C++)_翟天保Steven的博客-CSDN博客

3.16 迭代器模式

       迭代器模式是一种行为型的软件设计模式,提供一种方法能顺序访问聚合对象中的各个元素,而又不暴露其内部。

       我们使用的聚合对象各种各样,比如vector、list、tree、map等等,既然是聚合,那就有访问其个体的需要。而遍历访问这个行为可能有深度优先、广度优先、顺序遍历、逆序遍历等等,迭代器的意义就是将这个行为抽离封装起来,这样客户端只需要调用合适的迭代器,来进行对应的遍历,而不用自己去实现这一行为。

       迭代器模式的优点:

  1. 符合单一职责原则。将遍历行为抽离成单独的类。
  2. 符合开闭原则。添加新集合或者新迭代器,不改变原有代码。
  3. 便于扩展多种遍历行为
  4. 访问数据又不暴露内部

       迭代器模式的缺点:

  1. 若对聚合对象只需要进行简单的遍历行为,那使用迭代器模式有些大材小用
  2. 系统复杂性提高,类数量较多

       举例:容器遍历,具体代码实现详情见:

设计模式之迭代器模式(C++)_翟天保Steven的博客-CSDN博客

3.17 中介者模式

       中介者模式是一种行为型的软件设计模式,也称为仲裁者模式,顾名思义,该模式的作用就是中介,帮助其他类进行良好的交流。

       类之间关系混乱的时候,会有很强的耦合性。就拿租房为例,如果有10个租客,5个房东,他们各自单线联系,那么他们之间的交流会相对混乱,维护起来也复杂的多。此时出现了一个房产中介,它将租客和房东的信息整合起来,由中介安排合适的租客和房东交流,那么交流就变得高效且清晰,当然也要付费的(就像增加中介类的代码维护开销一样)。

       中介者模式的优点:

  1. 解耦。中介的存在使得同事对象间的强耦合关系解除,它们可以独立地变化而不会影响到整体,便于被复用。
  2. 良好扩展性。交互行为发生改变,只需要扩展中介即可。
  3. 集中交互,便于管理

      中介者模式的缺点:

  1. 中介者的职责很重要,且复杂

      举例:房屋租赁,具体代码实现详情见:

设计模式之中介者模式(C++)_翟天保Steven的博客-CSDN博客

3.18 观察者模式

       观察者模式是一种行为型的软件设计模式,定义对象间的一种一对多的依赖关系,当被观察者状态发生改变时,所有观察者都做出相应改变。Qt中的信号与槽就是一种典型的观察者模式。

       观察者模式的优点:

  1. 耦合双方依赖于抽象,不需要了解具体
  2. 提供了稳定的信息更新传递机制
  3. 良好扩展性

      观察者模式的缺点:

  1. 当观察者过多时,挨个通知观察者消息可能会花费较长时间
  2. 某个观察者出现卡顿,可能影响整个进程,一般采用异步机制解决,但同时也要注意程序安全

      举例:开车等红绿灯,具体代码实现详情见:

设计模式之观察者模式(C++)_翟天保Steven的博客-CSDN博客

3.19 备忘录模式

       备忘录模式是一种行为型的软件设计模式,在不破坏封装的前提下,获取一个对象的内部状态,并在对象外保存该状态,当对象需要恢复到该状态时,对其进行恢复。

       备忘录模式的优点:

  1. 良好封装性。发起人对象中的内部状态被保存在备忘录中,也只能由自己读取,对其他对象起到了屏蔽作用。
  2. 提供了状态恢复机制。类似于游戏存档读档。
  3. 简化了发起人职责。发起人状态的存储和获取,被分离出去了。

       备忘录模式的缺点:

  1. 资源消耗较大,对发起人对象不同内部状态的存储,会导致开销增加

       举例:存读游戏进度,具体代码实现详情见:

设计模式之备忘录模式(C++)_翟天保Steven的博客-CSDN博客

3.20 状态模式

       状态模式是一种行为型的软件设计模式,当一个对象的内在状态改变时,其行为也随之改变。就像玩游戏的时候,不同的buff状态,角色会有不同的伤害、技能等等。

       当控制一个对象状态的条件表达式过于复杂时,很适合用该模式,将复杂的判断逻辑转移到表示不同状态的系列类中,能将逻辑大大简化。

       状态模式的优点:

  1. 良好封装性。每个状态的行为被封装到对应类中。
  2. 便于维护。减少了if else或switch语句的出现,适用于条件判断复杂的场景。
  3. 良好扩展性。添加状态更便捷。

      状态模式的缺点:

  1. 状态数量增加,类数量也会增加,对开发者要求较高
  2. 状态少时,应用状态模式会显得冗余

       举例:开关灯,具体代码实现详情见:

设计模式之状态模式(C++)_翟天保Steven的博客-CSDN博客

还有三个模式等待更新。。。。。。

四、总结

       上述讲了这么多关于设计模式的内容,希望我举的例子能帮助你相对容易地理解设计模式。每个模式的链接点进去,都是专门的一篇文章,且附带了完整详细简单的源码,相信多少会给你带来一些帮助!

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/440071.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第18章_JDK8-17新特性(上)

第18章_JDK8-17新特性(上) 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 1. Java版本迭代概述 1.1 发布特点(小步快跑,快速迭代…

安装TortoiseGit后桌面文件夹和用户文件夹中显示红色叹号

✨ TortoiseGit作为一个很好用的git图形化工具,能够很方便的进行版本控制。但在安装这个软件之后就遇到了一个问题。 👀问题描述:我们知道,安装TortoiseGit之后,当自己版本库中文件发生更改,但还没有提交到受控库时&am…

信息系统管理工程师好考吗?如何去备考呢?

信息系统管理工程师有点难度,侧重于IT技术的中级资格考试,主要适合系统管理员等专业技术人员去考 一、信息系统管理工程师考试介绍: 考试科目有两科,且成绩不延续,考试都合格后的证书是永久有效的。 报考条件&#x…

文件改名,如何将文件复制到指定文件夹里,并设置自动编号同名文件

在工作中,我们经常会遇到要将文件进行备份的时候,那么文件名称相同的情况下要如何批量备份呢?又如何自动编号同名文件?今天小编就给大家分享一下我的操作办法。 首先,第一步我们要进入“文件批量改名高手”的主页面并…

pot lib:optimal transport python库

文章目录 transport1. [计算最优传输(Computational Optimal Transport)](https://zhuanlan.zhihu.com/p/94978686)2. 离散测度 (Discrete measures), 蒙日(Monge)问题, Kantorovich Relaxation (松弛的蒙日问题)3. scipy.stats.wasserstein_distance 距…

CVPR2023活体检测Instance-Aware Domain Generalization for Face Anti-Spoofing学习笔记

论文链接:https://arxiv.org/pdf/2304.05640.pdf 代码链接:GitHub - qianyuzqy/IADG: (CVPR 2023) Instance-Aware Domain Generalization for Face Anti-Spoofing(尚未公布) 研究动机 此前的基于域泛化(domain gen…

信号完整性分析:关于传输线的三十个问题解答(二)

11.对于 50 欧姆带状线的纵横比,什么是好的经验法则?(What is a good rule of thumb for the aspect ratio of a 50-Ohm stripline?) 在带状线几何形状和 FR4 基板中,线宽和平面之间的电介质间距的纵横比为 。由于有两个平面,带…

如何快速建立一个podman环境

本文介绍如何安装podman,并创建podman容器 环境 Centos8 安装podman Podman 是一个容器环境,首先在主机上安装 Podman。执行下面命令来安装podman: [rootlocalhost ~]# yum -y install podman然后修改一下用户命名空间的大小&#xff1a…

202305读书笔记|《因思念而沉着》——任何赞美都是身外之物唯自由可随身携带

《因思念而沉着》作者巴哑哑,忘了是什么时候加入的书架,昨天下班地铁上读完的书。是美的! 部分节选如下: 羽叶茑萝举着熄灭的花青色的小枣落了一地所以哭泣沾染上了你的脸 在没落下 当我们开始生活 就是开始患上了眼疾你独自悲伤…

互联网求职指南2023版(内含腾讯、阿里、字节真实面经)

0. 缘起 五年前,小编曾经写过一篇文章,文章主要带大家了解当时的互联网巨头公司、独角兽公司。并给出了互联网的求职建议。从简历、知识储备、暑期实习、面试上给出了中肯的建议。现在看来也是很受用的,感兴趣的可以点击链接《互联网求职指南…

【人工智能概论】 构建神经网络——以用InceptionNet解决MNIST任务为例

【人工智能概论】 构建神经网络——以用InceptionNet解决MNIST任务为例 文章目录 【人工智能概论】 构建神经网络——以用InceptionNet解决MNIST任务为例一. 整体思路1.1 两条原则1.2 四个步骤 二. 举例——用InceptionNet解决MNIST任务2.1 模型简介2.2 MNIST任务2.3 完整的程序…

【ChatGPT 】ChatGPT Sidebar 实战:自定义 ChatGPT 搜索页面回复模板(示例开发和文员专用模板)

目录 一、前言 二、ChatGPT Sidebar 通用配置 (1)通用配置入口 (2)设置 ① 如何访问 ChatGPT ② 语言 ③ 主题 三、ChatGPT Sidebar 搜索页面 (1)搜索页面入口 (2)设置 …

Node 05-Node.js模块化

Node.js 模块化 介绍 什么是模块化与模块 ? 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化 其中拆分出的 每个文件就是一个模块,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用…

【问题解决】Git报错:failed to push some refs to xxxxx

Git报错:failed to push some refs to xxxxx To https://xxxxxxxxxxxx.git ! [rejected] master -> master (fetch first) error: failed to push some refs to ‘https://xxxxxxxx.git’ hint: Updates were rejected because the remote contains work that yo…

AI+明厨亮灶智能算法 yolo

AI明厨亮灶智能算法通过pythonyolo网络模型分析算法,AI明厨亮灶模型算法可接对后厨实现如口罩识别、厨师服穿戴、夜间老鼠监测、厨师帽识别、厨师玩手机打电话识别、抽烟识别等实时分析监测。Python是一种由Guido van Rossum开发的通用编程语言,它很快就…

领导力专题︱聊聊领导力的主要问题

本文内容结构 一、领导力的主要问题:领导者与下属 1、让人敬佩的领导者的能力与技巧 2、下属的期望 (1)热情 (2)重视 (3)欣赏 (4)归属感 3、下属(追随…

微结构MRI参数估计的神经网络:在白质扩散-弛豫模型中的应用

导读 通过使用生物物理模型来解释弛豫-扩散MRI大脑数据,可以研究白质微观结构的具体特征。尽管更复杂的模型有可能揭示组织的更多细节,但也会导致参数估计耗时较长,由于简并拟合地形中普遍存在局部最小值,这些参数估计可能会收敛…

软件测试工程师需要达到什么水平才能顺利拿到 20k 无压力?

最近有粉丝朋友问:软件测试员需要达到什么水平才能顺利拿到 20k 无压力? 这里写一篇文章来详细说说: 目录 扎实的软件测试基础知识:具备自动化测试经验和技能:熟练掌握编程语言:具备性能测试、安全测试、全…

前端Vue.js项目开发,不重启项目,快速切换后台地址---使用nginx负载简单快速实现更换后台代理地址

前端Vue.js项目开发,不重启项目,快速切换后台地址—使用nginx负载简单快速实现更换后台代理地址 本文实现了在vue项目不重启的情况下,快速实现更换联调后台服务器的方法, 能够大大节省vue项目重启时间 chen 2023-04-20 文档源码地址,最新版本会在这里修改…

互交式3d地球仪工具:Earth 3D - World Atlas Mac

Earth 3D - World Atlas for Mac是一款3d地球仪。这个交互式 3D 地球仪以世界奇观、政治和物理地图以及天气为特色。发现许多关于我们星球的有趣事实和有用信息!原始的彩色图形、用户友好的界面和准确的信息——这就是 Earth 3D - World Atlas 的全部意义所在&#…