[GN] 23种设计模式 —— 常见设计模式学习总结

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 创建型模式 —— 创建的艺术
  • 结构型模式 —— 组合的艺术
    • 适配器模式 -- 不兼容结构的协调
      • 对象适配器
      • 类适配器模式
      • 优缺点
      • 适用场景
    • 组合模式 -- 树形结构的处理
      • 例子
      • 优缺点
      • 适用场景
    • 外观模式 -- 提供统一入口
      • 例子
      • 优缺点
      • 适用场景
    • 代理模式 -- 对象的间接访问
      • 常用的几种代理模式
      • 优缺点
      • 适用场景
  • 行为型模型 —— 交互的艺术
    • 命令模式 -- 请求发送者与接受者解耦
      • 例子
      • 优化
      • 优缺点
      • 适用场景
    • 迭代器模式 -- 遍历聚合对象中的元素
    • 观察者模式 -- 对象间的联动
      • 例子
      • todo 与JAVA一系列实用关系
      • 适用场景
    • 策略模式 -- 算法的封装与切换
      • 例子
      • 优缺点
      • 适用场景
  • 总结


前言

[GN] 23种设计模式 —— 常见设计模式学习总结_第1张图片
本编文章仅为个人学习 23种模式中‘常见’的设计模式,并非全部。


创建型模式 —— 创建的艺术

见本人上篇博客

结构型模式 —— 组合的艺术

适配器模式 – 不兼容结构的协调

对象适配器

[GN] 23种设计模式 —— 常见设计模式学习总结_第2张图片

Target – 目标抽象类
Adapter – 适配器类 继承Target
Adaptee 适配者类 与Adapter关联关系

类适配器模式

[GN] 23种设计模式 —— 常见设计模式学习总结_第3张图片

Target – 目标抽象类
Adapter – 适配器类 实现Target接口
Adaptee 适配者类 继承Adapter类

优缺点

优点
(1)将目标类和适配者类解耦。通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
(2) 增加了类的透明性和复用性。将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者类的复用性,同一个适配者类可以在多个不同的系统中复用。
(3) 灵活性和扩展性都非常好。通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的
缺点
(1)对于Java语言不支持多继承,一次最多只能适配一个适配者类
(2)适配者类不能为最终类,例如在Jovo中不能为final类,
(3) 在Javac #等语言中,类适配器模式中的目标抽象类只能为接口,不为类,其使用有一定的局限性。

适用场景

(1) 系统需要使用一些现有的类,而这些类的接口(例如方法名)不符合系统的需要,甚至没有这些类的源代码。
(2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。

组合模式 – 树形结构的处理

[GN] 23种设计模式 —— 常见设计模式学习总结_第4张图片

对文件和文件夹操作 增加文件、删除文件、查询文件

例子

结构图

[GN] 23种设计模式 —— 常见设计模式学习总结_第5张图片

Component(抽象构件)︰

  • 它可以是接口或抽象类,为叶子构件和容器构件对象声明接口
  • 在该角色中可以包含所有子类共有行为的声明和实现。例如增加子构件、删除子构件、获取子构件等。

Leaf (叶子构件)︰

  • 它在组合模式结构中表示叶子节点对象。
  • 叶子节点没有子节点,它实现了在抽象构件中定义的行为

Composite (容器构件)︰

  • 它在组合模式结构中表示容器节点对象。容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点
  • 它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法
  • 在其业务方法中可以递归调用其子节点的业务方法(operation)。

具体实例
[GN] 23种设计模式 —— 常见设计模式学习总结_第6张图片

AbstractFile充当抽象构件类
Tolder充当容器构件类
ImogeFile、TextFile和VideoFile充当叶子构件类。

优缺点

“单位”充当了抽象构件角色,“公司”充当了容器构件角色,“研发部”“财务部”和“人力赉源部”充当了叶子构件角色。

优点

(1)组合模式可以清楚地定义分层次的复杂对象。它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
(2) 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
(3) 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合开闭原则。

缺点
在增加新构件时很难对容器中的构件类型进行限制。有时希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只个包函文本文件。使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自相同的抽象层

适用场景

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

外观模式 – 提供统一入口

为多个业务类提供了统一的入口,简化了类与类之间的交互
调用某功能时候,多个类的集合一同出现。
没有外观角色,每个客户端可能需要和多个子系统之间进行复杂的交互,系统耦合度很大。
[GN] 23种设计模式 —— 常见设计模式学习总结_第7张图片

子系统是一个广义的概念。可能是一个类,可能是功能模块

例子

[GN] 23种设计模式 —— 常见设计模式学习总结_第8张图片

Facade (外观角色): 在客户端可以调用这个角色的方法,可以调用这个角色的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任。
SubSystem (子系统角色):在软件系统中可以有一个或者多个子系统角色。每个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功钱。每个子系统都可以被客户端直接调用,或着被外观角色调用

优缺点

优点

  • 对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目。通过引入外观模式,客户端代码变得简单,与之关联的对象也很少。
  • 实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。
  • 一个子系统的修改对其他子系统没有任何影响

缺点

  • 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性。
  • 如果设计不当,增加新的子系统可个需要修改外观类的源代码,这违背了开闭原则。

适用场景

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

代理模式 – 对象的间接访问

在代理模式中引入了一个新的代理对象,代理对象可以在客户端对象和目标对象之间起到中介的作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。
[GN] 23种设计模式 —— 常见设计模式学习总结_第9张图片

( 1 ) Subject (抽象主题角色): 它声明了真实主题和代理主题的共同接口,使得在任向使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
( 2 ) Proxy( 代理主题角色): 代理主题角色内部包仓了对真实主题的引用,从而可以在任同时候操作真实主题对象。在代理主题角色中提供一个与真实主题角色相同的接口,以便在任向时候都可以替代真实主题。
( 3) RealSubject (真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

常用的几种代理模式

  • 远程代理(Renote Proxy): 为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中。远程代理又称为大使( Ambossodor ) 。
  • 虚拟代理(V)rtuol Prox) : 如果需要创建一个贵源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
  • 保护代理(Protect Prox): 控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。------ 例:登录验证
  • 缓冲代理(Coche Proxy)︰为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些
  • 智能引用代理(Smart ReferenceProx): 当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。----- 例:打日志

代理模式和装饰模式在实现时有些类似,
代理模式主要是给真实主题类增加一些全新的职责,例如权限控制、缓冲处理、智能引用、远程访问等,这些职责与原有职责不属于同一个问题域
装饰模式是通过装饰类为具体构件类增加一些相关的职责,是对原有职责的扩展,这些职责属于同一问题域。

JAVA提供的动态代理 先留个坑 后面再学 -CGlib

优缺点

优点
(1) 代理模式能够协调调用考和被调用者,在一定程度上降低了系统的耦合度,满足迪米特法则。
(2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码。
缺点
(1) 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
(2) 实现代理模式需要额外的工作,有些代理模式的实现非常复杂,例如远程代理。

适用场景

(1) 当客户端对象需要访问远程主机中的对象时,可以使用远程代理。
(2) 当需要用一个消耗资源较少的对象来代表一个消耗贵源较多的对象,从而降低系统开销、缩短运行时间时,可以使用虚拟代理。
(3) 当需要控制对一个对象的访问,为不同用户不同级别的访问权限时,可以使用保护代理。
(4)当需要为某一个被顿繁访问的操作结界提供一个临时存储空间,以供多个客户端共享访问这些结果时,可以使用缓冲代理。通过缓冲代理,系统无须在客户端每次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时,可以使智能引用代理

行为型模型 —— 交互的艺术

命令模式 – 请求发送者与接受者解耦

发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如向发送请求,而不必知道如同完成请求。

[GN] 23种设计模式 —— 常见设计模式学习总结_第10张图片
Invoker:
调抽象类(command = new XXXCommand 调command.execute) —>
XXXCommand:
具体类已经实现对应具体方法(XXXCommand的execute内实现对应命令action)

Command(抽象命令类): 抽象命令类一般是一个抽象类或接口,在其中声明了用于抽行请求的execute ( )等方法,通过这些方法可以调用请求接收者的相关操作。
ConcreteCommand (具体命令类): 具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法。它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute ()方法时,将调用接收者对象的相关操作(Action )。
Invoker (调用者): 调用着即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令之间存在关联关系。
Receiver(接收者)︰接收着执行与请求相关的操作,它具体实现对请求的业务处理。

命令模式的本质是

  • 将发出命令的责任和执行命令的责任分割开。
  • 每个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。命令模式允许请求的一方和接收的一方独立开来

例子

典型的抽象命令类代码如下:
在这里插入图片描述

请求发送者即调用者
将针对抽象命令类进行编程,可以通过注入方式传入具体命令类对象
[GN] 23种设计模式 —— 常见设计模式学习总结_第11张图片

具体命令类继承了抽象命令类
与请求接收者相关联,实现了在抽象命令类中声明的execute ( )方法
并在实现时调用接收者的请求响应方法action ( )
在这里插入图片描述

请求接收者类具体实现对请求的业务处理
提供了action ( )方法,用于执行与请求相关的操作。
在这里插入图片描述

优化

  1. 多个请求时

需要命令队列CommandQueue类的实现
[GN] 23种设计模式 —— 常见设计模式学习总结_第12张图片
请求发送者Invoker将针对CommandQueue编程
[GN] 23种设计模式 —— 常见设计模式学习总结_第13张图片
2. 撤销操作实现

3. 请求日志

4. 宏命令(组合命令)

优缺点

优点
(1) 降低系统的耦合度。请求者与接收者之间实现宪全解耦,相同的请求者可以对应不同的接收者。同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
(2)新的命令可以很容易地加入系流中。由于增加新的具体命令类不会影响到其他类,无须修改原有系统源代码甚至客户类代码,满足开闭原则的要求。
(3) 为请求的撤销(撤销)和恢复(重做)操作提供了一种设计和实现方案。
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

适用场景

(1) 系统需要将请求调用者和请求接收着解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心同时被调用。
(2)系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初抬调用者可以有不同的生命期。换言之,最初的请求发出者可个已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
(3)系统需要支持命令的撤销(undo)操作和恢复( Redo)操作。
(4)系统需要将一组操作组合在一起形成宏命令。

迭代器模式 – 遍历聚合对象中的元素

在软件开发时,经常需要使用聚合对象来存储一系列数据。
聚合对象拥有两个职责:

  • 一是存储数据;
  • 二是遍历数据。

从依赖性来看,前者是聚合对象的基本职责﹔而后者既是可变化的,又是可分离的。因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中

[GN] 23种设计模式 —— 常见设计模式学习总结_第14张图片

(1) Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法。例: 获取第一个元素的first (),访问下一个元素的next (),是否还有下一个元素的hosNext (),获取当前元素的currentItem ()等。
(2) ConcreteIterator (具体迭代器)︰它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置。
(3) Aggregote (抽象聚合类): 它用于存储和管理元素对象,声明一个createIterator ( )方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
(4) ConcreteAggregate(具体聚合类): 它实现了在抽象聚合类中声明的createIterator ( )方法,该方法返回一个与该具体聚合类对应的具体迭代器Concretelterator实例

JAVA内置了迭代器 且掌握较熟悉,本模式就不具体介绍


观察者模式 – 对象间的联动

为了更好地描述对象之间存在的这种一对多(包括一对一)的联动,观察者模式应运而生。

在观察羞模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任同相互联系,可以根据需要增加和删除观察着,使得系统更易于扩展。
[GN] 23种设计模式 —— 常见设计模式学习总结_第15张图片

(1) Subject (目标类): 目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时定义了通知方法notify
(2) ConcreteSubject (具体目标): 具体目标是目标类的子类,通常包含有经常发生改变的数据。当它的状态发生改变时,向其各个观察着发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有)。如果无须扩展目标类,则具体目标类可以省略。
(3)Observer(观察者): 观察者将对观察目标的改变做出反应。观察者一般定义为接口,该接口声明了更新数据的方法update () ,因此又称为抽象观察者。
(4) ConcreteDbserver(具体观察者)︰在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。它实现了在抽象观察者0bserver中声明的update ()方法。通常在实现时,可以调用具体目标类的attach ( )方法将自己添加到目标类的集合中或通过detach ()方法将自己从目标类的集合中删除。

例子

首先定义一个抽象目标Subject
[GN] 23种设计模式 —— 常见设计模式学习总结_第16张图片

具体目标类ConcreteSubject是实现抽象目标类Subject的一个具体子类
[GN] 23种设计模式 —— 常见设计模式学习总结_第17张图片
抽象观察者角色一般定义为一个接口,通常只声明一个updote ( )方法
为不同观察者的更新(响应)行为定义相同的接口。
在这里插入图片描述

在具体观察者ConcreteObserver中实现updote ( )方法
在这里插入图片描述

todo 与JAVA一系列实用关系

后期补…

适用场景

(1) 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2) 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
(3) 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……可以使用观察者模式创建一种链式融发机制。

策略模式 – 算法的封装与切换

实现某一个功能有多条途径。每一条途径对应一种算法,此时可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。

策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开。将算法的定义放在专门的策略类中,每个策略类封装了一种实现算法。使用算法的环境类针对抽象策略类进行编程,符合依赖倒转原则。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。

策略模式:(Stratey Pattern)定义一系列算法类,将每一个算法封装起来并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化.也称为政策模式. ( Policv)

[GN] 23种设计模式 —— 常见设计模式学习总结_第18张图片

(1 ) Context(环境类): 环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
( 2 )Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类。它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
(3)ConereteStrategy(具体策略类)︰它实现了在抽象策略类中声明的算法。在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

例子

首先应该创建一个抽象策略类

将封装每一种具体算法的类作为该抽象策略类的子类
在这里插入图片描述

对于Context类而言,在它与抽象策略类之间建立一个关联关系
[GN] 23种设计模式 —— 常见设计模式学习总结_第19张图片

Context类中定义一个AbstroctStrategy类型的对象stratesy
通过注入的方式在客户端传入一个具体策略对象 --客户端代码
在这里插入图片描述

在客户端代码中只需注入一个具体策略对象。可以将具体策略类类名存储在配置文件中,通过反射来动态创建具体策略对象

优缺点

优点
(1) 策略模式提供了对开闭原则的完美支持。用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
(2) 策略模式提供了管理相关的算法族的巿法。策略类的等级结构定义了一个算法或行为族,怡当使用继承可以把公共的代码移到抽象策略类中
(3) 使用策略模式可以避免多重条件选择语句。
(4) 策略模式提供了一种算法的复用机制。由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

缺点
(l)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择怡当的算法。
(2)策略模式将造成系统产生很多具体策略类。细小的变化都将导致系统要增加一个新的具体策略类。
(3) 无法同时在客户端使用多个策略类。也就是说,在使用策略模式时,客户端每次只.能使用一个策略类

适用场景

(1) 一个系统需要动态地在几种算法中选择一种。可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。
(2)一个对象有很多的行为,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以锥护的多重条件选择语句。
(3)不希望客户端知道复杂的、与算法相关的数据结构。在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与审全性。

总结

欢迎大家指正交流 后期会完善本篇文章
桥接模式 装饰模式 状态模式 后期再补

你可能感兴趣的:(设计模式,设计模式,学习)