我是靠谱客的博主 傻傻菠萝,最近开发中收集的这篇文章主要介绍常见的23种设计模式总结一、创建型模式二、结构型模式三、行为型模式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、创建型模式

1、工厂模式

1.1 简单工厂模式 详情

在这里插入图片描述

何时使用:对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。如代码中有一堆的 if-else 分支判断这种情况。

优点

  • 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  • 客户端无需知道所创建具体产品的类名,只需知道参数即可。

缺点

  • 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
  • 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
  • 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
  • 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

1.2 工厂方法模式 详情

在这里插入图片描述
何时使用:单个对象本身的创建过程比较复杂,比如要组合其他类对象,做各种初始化操作。在这种情况下,也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中。

优点

  • 封装复杂的创建逻辑,调用者无需了解如何创建对象。
  • 将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

1.3 抽象工厂模式 详情

在这里插入图片描述

何时使用:系统的产品有多个的产品族与多个厂商,而系统只消费其中某一厂商的产品。如产品族(product)有手机、耳机、平板、电脑等;厂商(factory)有小米、华为、苹果等。我们只要华为厂商的产品。

优点

  • 一个厂商中的多个产品族被设计成一起工作时,它能保证客户端始终只使用同一个厂商中的对象。
  • 厂商扩展很容易。

缺点:产品族扩展非常困难,要增加一个产品族,既要修改抽象工厂里加代码,又修改具体工厂里面加代码;

2、单例模式 详情

在这里插入图片描述

何时使用:当您想控制实例数目,节省系统资源的时候。

优点

  • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

3、建造者模式 详情

在这里插入图片描述
何时使用:适用于一个具有较多的零件的复杂产品的创建过程,由于需求的变化,组成这个复杂产品的各个零件经常猛烈变化,但是他们的组合方式却相对稳定。

优点

  • 封装性好,构建和表示分离。
  • 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  • 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

缺点

  • 产品的组成部分必须相同,这限制了其使用范围。
  • 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

4、原型模式 详情

在这里插入图片描述

何时使用

  • 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  • 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  • 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。

优点

  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
  • Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。

缺点

  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
  • 需要为每一个类都配置一个 clone 方法

二、结构型模式

1、适配器模式 详情

在这里插入图片描述

何时使用

  • 已经存在的类,他的方法和需求不匹配(方法结果相同或相似)的情况。需要将一个类的接口转换成客户希望的另外一个接口。例如电源转换头、手机充电转换头、显示器转接头。
  • 适配器模式不是软件设计阶段考虑的设计模式,适用于软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。有点亡羊补牢的感觉。

优点

  • 能提高类的透明性和复用,现有的类复用但不需要改变。
  • 目标类和适配器类解耦,提高程序的扩展性。
  • 在很多业务场景中符合开闭原则。

缺点

  • 过多地使用适配器,会让系统非常零乱,不易整体进行把握。

2、桥接模式 详情

在这里插入图片描述
何时使用

  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
  • 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  • 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

优点

  • 实现抽象和实现的分离
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统
  • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法

缺点

  • 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

3、过滤器模式 详情

在这里插入图片描述
何时使用:需要对数据进行特殊处理,处理逻辑与业务逻辑解耦的情况

优点

  • 将对象的过滤、校验逻辑抽离出来,降低系统的复杂度。
  • 过滤规则可实现重复利用。

缺点

  • 性能较低,每个过滤器对每一个元素都会进行遍历。如果有n个元素,m个过滤器,则负责度为O(mn)

4、组合模式 详情

在这里插入图片描述
何时使用

  • 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
  • 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
  • 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。

优点

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

缺点:破坏了“单一职责原则”

5、装饰器模式 详情

在这里插入图片描述

何时使用

  • 当我们需要在不影响其他类的状况下,以动态、透明的方式为类添加功能;
  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。

优点

  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
  • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
  • 装饰器完全遵守开闭原则

缺点

  • 从代码层面来看,使用装饰器模式会出现更多的代码,更多的类,增加程序复杂性
  • 动态装饰时,多层装饰时会更复杂

6、外观模式 详情

在这里插入图片描述
何时使用

  • 当要为访问一系列复杂的子系统提供一个简单统一的入口时可以使用外观模式。
  • 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而是通过外观类建立联系,降低层之间的耦合度。

优点

  • 对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展。
  • 对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复
    杂性。

缺点

  • 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则
  • 不能很好地限制客户使用子系统类,很容易带来未知风险。

7、享元模式 详情

在享元模式中可以共享的相同内容称为内部状态(享元角色),而那些需要外部环境来设置的不能共享的内容称为外部状态(非享元角色)
在这里插入图片描述

何时使用

  • 一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费;
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中(细粒度对象);
  • 使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。

优点

  • 它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份;
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  • 如果没有注意划分外部状态和内部状态,则可能会引起线程安全问题;
  • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化;
  • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

8、代理模式 详情

在这里插入图片描述

何时使用:在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。

优点

  • 实现了访问者与访问对象之间的解耦。
  • 代理模式在应用层与对象之间起到中介作用,保护了对对象的访问。
  • 职责清晰(单一职责):真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,代理模式可以在代理过程中增加逻辑,如Spring框架的AOP。

缺点

  • 增加代理会使程序请求处理变慢。
  • 类的数量变多,系统更加复杂。

三、行为型模式

1、责任链模式 详情

在这里插入图片描述
何时使用

  • 允许动态的增加和删除职责,且由用户决定是否执行某种操作。
  • 实现了被操作对象和操作的解耦。
  • 一个命令可以被多个处理器执行,例如各种框架中的拦截器

优点

  • 允许动态的增加和删除职责,且由用户决定是否执行某种操作。
  • 实现了被操作对象和操作的解耦。
  • 一个命令可以被多个处理器执行,例如各种框架中的拦截器

缺点

  • 系统复杂度提高,容易出错。即组链时候不合理,可能导致请求得不到执行,还有可能将链变成一个环,请求在里面循环,永远都完不了。
  • 影响性能,出现递归调用,容易造成栈溢出。

2、命令模式 详情

将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁,是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:"请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
在这里插入图片描述
何时使用

  • 当想要使用操作参数化对象时,请使用命令模式。
  • 当想要对操作进行排队、安排它们的执行或远程执行它们时,请使用命令模式。
  • 当想要实现可逆操作时,请使用命令模式。

优点

  • 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令。
  • 容易实现对请求的撤销和重做

缺点:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意

3、解释器模式 详情

在这里插入图片描述

何时使用

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  • 一些重复出现的问题可以用一种简单的语言来进行表达。
  • 对执行效率要求不高。

优点:扩展性强。如果增加了新的语法,只需要增加对应的解释器。

缺点

  • 当文法规则比较复杂时,会引起类膨胀,比较难维护。如果出错了,调试比较困难。
  • 执行效率比较低下。因为当表达式比较复杂,结果层层依赖的话会采用递归方式进行解析。

4、迭代器模式 详情

在这里插入图片描述

何时使用

  • 访问一个聚合对象的内容而无须暴露它的内部表示。
  • 需要为聚合对象提供多种遍历方式。
  • 为遍历不同的聚合结构提供一个统一的接口。

优点

  • 访问一个聚合对象的内容而无须暴露它的内部表示。
  • 遍历任务交由迭代器完成,这简化了聚合类。
  • 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
  • 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  • 封装性良好,为遍历不同的聚合结构提供一个统一的接口

缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

5、中介者模式 详情

在这里插入图片描述

何时使用

  • 如果一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。

  • 如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象。可以采用中介者模式,把这个对象跟其它对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了。

优点

  • 中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互补依赖。这样一来,同事对象就可以独立地变化和复用,而不再像以前那样“牵一处而动全身”了。

  • 多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那么就扩展中介者对象,而各个同事类不需要做修改。

  • 没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象以后,中介者对象和同事对象的关系通常变成双向的一对多,这会让对象的关系更容易理解和实现。

缺点:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。

6、备忘录模式 详情

在这里插入图片描述
何时使用

  • 需要保存和恢复数据的相关场景;
  • 提供一个可回滚的操作,如ctrl+z、浏览器回退按钮、Backspace键等;
  • 需要监控的副本场景;

优点

  • 状态恢复机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
  • 信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。

缺点:如果需要保存的状态过多时,每一次保存都会消耗很多内存(所以可以结合原型模式使用)。

7、观察者模式 详情

在这里插入图片描述

何时使用

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
  • 事件多级触发场景。
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制

优点

  • 一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则。
  • 观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,并不认识一个具体的观察者,它只知道他们都有一个共同的接口。

缺点

  • 观察者模式的缺点在于如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者。
  • 如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃。
  • 观察者模式没有对应的机制让观察者知道被观察者对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

8、状态模式 详情

在这里插入图片描述

何时使用

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

优点

  • 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  • 封装性很好,这是状态模式的基本要求,状态变换放置在类的内部来实现,外部的调用不知道如何实现状态与行为的变换。
  • 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

缺点

  • 状态模式的使用必然会增加系统的类与对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
  • 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

9、策略模式 详情

在这里插入图片描述
何时使用

  • 针对同一种类型问题,有多种处理方式,每一种都能独立解决问题。
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景

优点

  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 使用策略模式可以避免使用多重条件转移语句。

缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一 定程度上减少对象的数量
  • 只适合扁平的算法结构

10、模板模式 详情

注意:为防止恶意操作,一般模板方法都加上final关键词。
在这里插入图片描述

何时使用

  • 有多个子类共有的方法,且逻辑相同。
  • 重要的、复杂的方法,可以考虑作为模板方法。

优点

  • 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
  • 将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。
    缺点
  • 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
  • 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。

11、访问者模式 详情

凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
在这里插入图片描述

何时使用

  • 当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。
  • 访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。
  • 有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。

优点

  • 分离无关行为,通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
  • 扩展性好,在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能(即添加访问者)。

缺点

  • 对象结构变化很困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  • 违反了依赖倒置原则,访问者类依赖了具体的宠物类(如依赖具体的宠物狗),而没有依赖抽象类。

最后

以上就是傻傻菠萝为你收集整理的常见的23种设计模式总结一、创建型模式二、结构型模式三、行为型模式的全部内容,希望文章能够帮你解决常见的23种设计模式总结一、创建型模式二、结构型模式三、行为型模式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部