行为型模式

267 阅读30分钟

行为型模式关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责

image.png

职责链模式

职责链模式,避免将请求发送者和接收者耦合在一起,让多个对象都有机会接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式

职责链模式的核心在于引入了一个抽象处理者

image.png

职责链模式的结构图包含两个角色

  • 抽象处理者(Handler),它定义了一个处理请求的接口,一般设计为抽象类
  • 具体处理者(ConcreteHandler),它是抽象处理者的子类,可以处理用户请求

职责链模式可分为纯的职责链模式和不纯的职责链模式

  • 纯的职责链模式,要求一个具体处理者对象只能在两个行为中选择一个:要么承担全部责任,要么将责任推给下家
  • 不纯的职责链模式,允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某个请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收

职责链模式的优点

  • 职责链模式使得一个对象无需知道是其它哪一个对象处理其请求
  • 请求处理对象仅需要维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接
  • 在给对象分派职责时,职责链可以提供更多的灵活性,可以通过在运行时对链进行动态地增加或修改来增加或改变处理一个请求的职责
  • 在系统中增加一个新的具体请求处理者时,无需修改原有系统的代码,只需要在客户端重新建立链接即可

职责链模式的缺点

  • 由于一个请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理
  • 对于比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便
  • 如果建链不当,可能会造成循环调用,将导致系统陷入死循环

职责链模式的适用场景

  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定
  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
  • 可动态指定一组对象处理请求

命令模式

命令模式可以将请求发送者和接收者完全解耦。发送者和接收者之间没有直接的引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求

命令模式,将一个请求封装成一个对象,从而可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作模式或者事物模式

image.png

命令模式结构图包含 4 个角色

  • 抽象命令类(Command),一般是一个抽象类或接口,在其中声明了用于执行请求的 execute 等方法,通过这些方法可以调用请求接收者的相关操作
  • 具体命令类(ConcreteCommand),具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法。它对应具体的接收者对象,将接收者对象的动作绑定其中
  • 调用者(Invoker),调用者即请求发送者,它通过命令对象来执行请求
  • 接收者(Receiver),接收者执行和请求相关的操作,它具体实现对请求的业务处理

命令模式的本质是对请求进行封装。命令模式的关键在于引入了抽象命令类。


当一个请求发送者发送一个请求时,不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。此时,可以通过命令队列的方式来实现

命令队列的实现有多种形式。其中最常用、灵活性最好的一种方式是增加一个 CommandQueue 类,负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者


宏命令又称为组合命令,它是组合模式和命令模式联用的产物。宏命令是一个具体命令类,它拥有一个集合属性,在该集合中包含了对其它命令对象的引用

image.png

命令模式的优点

  • 降低系统的耦合度
  • 新的命令可以很容易的加入系统中
  • 可以比较容易的设计一个命令队列或宏命令
  • 为请求的撤销和恢复操作提供了一种设计和实现方案

命令模式的缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类

命令模式的适用场景

  • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
  • 系统需要在不同的时间指定请求、将请求排队和执行请求
  • 系统需要执行命令的撤销和恢复操作
  • 系统需要将一组操作组合在一起形成宏命令

解释器模式

解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子


抽象语法树(AST),以图形方式来直观的表示语言的构成

image.png

解释器模式,定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的语言是指使用规定格式和语法的代码。解释器模式是一种类行为型模式

由于表达式可分为终结符表达式和非终结符表达式,因此解释器模式的结构和组合模式的结构有些类似

image.png

解释器模式结构图包含 4 个角色

  • 抽象表达式(AbstractExpression),在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类
  • 终结符表达式(TerminalExpression),是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例
  • 非终结符表达式(NonterminalExpression),也是抽象表达式的子类,它实现了文法中非终结符的解释操作。由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成
  • 环境类(Context),又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句
    • 在解释器模式中,环境类用于存储解释器之外的一些全局信息。它通常作为参数被传递到所有表达式的解释方法 interpret 中,可以在 Context 对象中存储和访问表达式解释器的状态,向表达式解释器提供一些全局的、公共的数据。同时,还可以在 Context 中增加一些所有表达式解释器都共有的功能,减轻解释器的职责

解释器模式的优点

  • 易于改变和扩展文法
  • 每一条文法规则都可以表示为一个类,因此可以方便的实现一个简单的语言
  • 实现文法较为容易
  • 增加新的解释表达式较为方便

解释器模式的缺点

  • 对于复杂文法难以维护
  • 执行效率较低

解释器模式的适用场景

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

迭代器模式

在软件开发中,也存在大量类似电视机一样的类,它们可以存储多个成员对象,这些类通常称为聚合对象

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

  • 存储数据
  • 遍历数据

从依赖性上看,存储数据是聚合对象的基本职责;遍历数据既是可以改变的,又是可以分离的。因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称为「迭代器」的对象中

迭代器模式,提供一个方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标。迭代器模式是一个对象行为型模式

在迭代器模式结构中包含聚合和迭代器两个层次结构

image.png

在迭代器模式结构图中包含 4 个角色

  • 抽象迭代器(Iterator),它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法
  • 具体迭代器(ConcreteIterator),它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置
  • 抽象聚合类(Aggregate),它用于存储和管理元素对象,声明一个 createIterator 方法用于创建一个迭代器对象,充当抽象迭代器工厂角色
  • 具体聚合类(ConcreteAggregate),它实现了在抽象聚合类中声明的 createIterator 方法,该方法返回一个与该具体聚合类对应的具体迭代器 ConcreteIterator 实例

在迭代器模式结构图中可以看出,具体迭代器类和具体聚合类之间存在双重关系,其中一个关系为关联关系。在具体迭代器中需要维持一个对具体聚合对象的引用,该关联关系的目的是访问存储在聚合对象中的数据,以便迭代器能够对这些数据进行遍历操作

迭代器模式的优点

  • 支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。
  • 迭代器简化了聚合类
  • 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无需修改原有代码,满足开闭原则的要求

迭代器模式的缺点

  • 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性
  • 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展

迭代器模式的适用场景

  • 访问一个聚合对象的内容而无需暴露它的内部标识
  • 需要为一个聚合对象提供多种遍历方式
  • 为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性的操作该接口

中介者模式

如果在一个系统中对象之间的联系呈现为网状结构,对象之间存在大量的多对多联系,将导致系统非常复杂,这些对象既会影响别的对象,也会被别的对象所影响,这些对象被称为同时对象,它们之间通过彼此的相互作用实现系统的行为。

中介者模式可以使对象之间的关系数量急剧减少。通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构。

中介者模式,用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式

在中介者模式中,引入了用于协调其它对象/类之间相互调用的中介者类

image.png

在中介者结构图中包含四个角色

  • 抽象中介者(Mediator),它定义一个接口,该接口用于与各同事对象之间进行通信
  • 具体中介者(ConcreteMediator),它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,维持了对各个同事对象的引用
  • 抽象同事类(Colleague),它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时维持了一个对抽象中介者类的引用,其子类可以通过该引用来和中介者通信
  • 具体同事类(ConcreteColleague),它是抽象同事类的子类。每个同事对象在需要和其它同事对象通信时,先与中介者通信,通过中介者来间接完成与其它同事类的通信

中介者模式的核心在于中介者类的引入。中介者类承担了两方面的职责

  • 中转作用(结构性)。通过中介者提供的中转作用,各个同事对象就不再需要显式的引用其它同事。
  • 协调作用(行为性)。中介者可以更进一步地对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做

中介者模式的优点

  • 中介者模式简化了对象之间的交互,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互
  • 中介者模式可将各同事对象解耦。中介者有利于各同事之间的松耦合,可以独立的改变和复用每一个同事和中介者,增加新的中介者和新的同事类都比较方便,更好的符合开闭原则
  • 可以减少大量同事子类生成

中介者模式的缺点

  • 在具体中介类中包含了大量同事之间的交互细节,可能会导致具体中介类非常复杂,使得系统难以维护

中介者模式的适用场景

  • 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解
  • 一个对象由于引用了其它很多对象并且对象和这些对象通信,导致难以复用该对象
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类

备忘录模式

备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便的回到一个特定的历史步骤。当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原

备忘录模式,在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以再以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为 Token

备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计

image.png

备忘录模式结构图包含 3 个角色

  • 原发器(Originator),它是一个普通类,可以创建一个备忘录,并存储其当前内部状态,也可以使用备忘录来恢复其内部状态
  • 备忘录(Memento),存储原发器的内部状态,根据原发器来决定保存哪些内部状态。
  • 负责人(Caretaker),又称为管理者,负责保存备忘录,但是不能对备忘录的内容进行操作或检查

备忘录模式的优点

  • 它提供了一种状态恢复的实现机制,使得用户可以方便的回到一个特定的历史步骤
  • 备忘录实现了对信息的封装。一个备忘录对象是一种原发器对象状态的表示,不会被其它代码所改动

备忘录模式的缺点

  • 资源消耗过大。如果需要保存的原发器类的成员变量太多,就不可避免的需要占用大量的存储空间,每保存一次对象状态都需要消耗一定的系统资源

备忘录模式的适用场景

  • 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时就能恢复到先前的状态,实现撤销操作
  • 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象

观察者模式

观察者模式用于建立对象和对象之间的依赖关系。一个对象发生改变时将自动通知其它对象,其它对象将相应做出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展

观察者模式,定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式别名包括发布-订阅模式、模型-视图模式、源-监听器模式、从属者模式。观察者模式是一种对象行为型模式

观察者模式结构中通常包括观察目标和观察者两个继承层次结构

image.png

观察者模式结构图包含 4 个角色

  • 目标(Subject),又称为主题,它是指被观察者的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时定义了通知方法。目标类可以是接口,也可以是抽象类或具体类
  • 具体目标(ConcreteSubject),它是目标类的子类,通常包含有经常发生变化的数据。当它的状态发生改变时,向其各个观察者发出通知
  • 观察者(Observer),它将对观察目标的改变做出反应。观察者一般定义为接口,该接口声明了更新数据的方法 update,因此又被称为抽象观察者
  • 具体观察者(ConcreteObserver),在具体观察者中维护一个指向目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。

MVC架构中应用了观察者模式。MVC 是一种架构模式,包含模型、视图和控制器。

  • 模型可对应于观察者模式中的观察目标
  • 视图对应于观察者
  • 控制器可充当两者之间的中介者

当模型层数据发生变化时,视图层将自动改变其显示内容

观察者模式的优点

  • 观察者模式可以实现表示层和数据逻辑层的分离
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合
  • 观察者模式支持广播通信
  • 观察者模式满足开闭原则的要求

观察者模式的缺点

  • 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能会导致系统崩溃
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

观察者模式的适用场景

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

状态模式

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时,可以使用状态模式

状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化

状态模式,允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象,状态模式是一种对象行为型模式

在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心

image.png

状态模式结构图包含 3 个角色

  • 环境类(Context),又称为上下文类,它是拥有多种状态的对象。
    • 由于环境类的状态存在多样性并且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类 state 的实例,这个实例定义当期状态,在具体实现时,它是一个 state 子类的对象
  • 抽象状态类(State),它用于定义一个接口以封装与环境类的一个特定状态相关的行为。
    • 在抽象状态类中声明各种不同状态对应的方法,而在其子类中实现这些方法。由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中
  • 具体状态类(ConcreteState),它是抽象状态类的子类。每个子类实现一个与环境类的一个状态相关的行为
    • 每个具体状态类对应环境类的一个具体状态,不同的具体状态类其行为有所不同

在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换。通常有以下两种实现状态转换的方式

  • 统一由环境类负责状态之间的转换
  • 由具体状态类来负责状态之间的转换

在某些情况下,多个环境对象可能需要共享同一个状态。如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员对象

状态模式的优点

  • 封装了状态的转换规则
  • 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为
  • 允许状态转换逻辑和状态对象合为一体,而不是提供一个巨大的条件语句块
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数

状态模式的缺点

  • 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大
  • 状态模式的程序结构和实现都较为复杂,如果使用不当将导致系统结构和代码的混乱,增加系统设计的难度
  • 状态模式对开闭原则的支持并不友好

状态模式的适用场景

  • 对象的行为依赖于它的状态,状态的改变将导致行为的变化
  • 在代码中包含大量与对象状态相关的条件语句

策略模式

策略模式的主要目的是将算法的定义和使用分开,也就是将算法的行为和环境分开

  • 将算法的定义放在专门的策略类中,每个策略类封装了一种实现算法
  • 使用算法的环境类针对抽象策略类进行编程,符合依赖倒转原则

策略模式,定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式。策略模式是一种对象行为型模式

image.png

策略模式结构图包含 3 个角色

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

策略模式的优点

  • 策略模式提供了对开闭原则的完美支持
  • 策略模式提供了管理相关的算法族的办法
  • 策略模式提供了一种可以替换继承关系的办法
  • 使用策略模式可以避免多重条件选择语句
  • 策略模式提供了一种算法的复用机制

策略模式的缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
  • 策略模式将造成系统产生很多具体策略类
  • 无法同时在客户端使用多个策略类

策略模式的适用场景

  • 一个系统需要动态的在几个算法中选择一种
  • 一个对象有很多的行为,如果不同恰当的模式,这些行为就只好使用多重条件选择语句来实现
  • 不希望客户端知道复杂的、与算法相关的数据结构

模板方法模式

在软件开发中,有时会遇到这种情况,某个方法的实现需要多个步骤,其中有些步骤是固定的,而有些步骤并不固定,存在可变性。为了提高代码的复用性和系统的灵活性,可以使用一种被称为模板方法模式的设计模式来对这类情况进行设计

在模板方法模式中,将实现功能的每一个步骤所对应的方法称为基本方法 而调用这些基本方法同时定义基本方法的执行次序的方法称为模板方法

模板方法模式,定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法模式是一种类行为型模式

image.png

模板方法模式结构图中包含两个角色

  • 抽象类(AbstracClass),在其中定义了一系列基本操作,这些基本操作可以是具体的,也可以是抽象的
  • 具体子类(ConcreteClass),它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作

一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法

基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种

  • 抽象方法
    • 一个抽象方法由抽象类声明,由其具体子类实现
  • 具体方法
    • 一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖,也可以直接继承
  • 钩子方法
    • 一个钩子方法由一个抽象类或具体类声明并实现,而其子类可能会加以扩展

模板方法模式的优点

  • 模板方法模式在父类中形式化的定义一个算法,而由它的子类来实现细节的处理
  • 模板方法模式是一种代码复用技术,它在类库设计中尤为重要
  • 模板方法模式可实现一种反向控制结构
  • 在模板方法模式中可以通过子类来覆盖父类的基本方法,不同的子类可以提供基本方法的不同实现,更换或增加新的子类很方便,符合单一职责原则和开闭原则

模板方法模式的缺点

  • 需要为每一个基本方法的不同实现提供一个子类

模板方法模式的适用场景

  • 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些可以实现的细节由其子类来实现
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复
  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制

访问者模式

在使用访问者模式时,被访问的元素通常不是单独存在的,它们存储在一个集合中,这个集合称为对象结构。访问者通过遍历对象结构实现对其中存储的元素的逐个操作

访问者模式,提供一个作用于某对象结构中的各元素的操作表示,它使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式

image.png

在访问者模式结构图中包含 5 个角色

  • 抽象访问者(Visitor),为对象结构中的每个具体元素类(ConcreteElement)声明一个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型
  • 具体访问者(ConcreteVisitor),它实现了每个由抽象访问者声明的操作,每个操作用于访问对象结构中的一种类型的元素
  • 抽象元素(Element),它一般是抽象类或接口,它定义一个 accept 方法,该方法通常以一个抽象访问者为参数
  • 具体元素(ConcreteElement),它实现了 accept 方法,在 accept 方法中调用访问者的访问方法以便完成对一个元素的操作
  • 对象结构(ObjectStructure),它是一个元素的集合,用于存放元素对象,并且提供了遍历其内部元素的方法

访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。访问者模式包括两个层次结构

  • 访问者层次结构,提供了抽象访问者和具体访问者
  • 元素层次结构,提供了抽象元素和具体元素

在访问者模式中,抽象访问者定义了访问元素对象的方法。通常为每一种类型的元素对象都提供一个访问方法,而具体访问者可以实现这些访问方法。这些访问方法的命名一般有两种方式

  • 直接在方法名中标明待访问元素对象的具体类型
  • 统一取名为 visit,通过参数类型的不同来定义一系列重载的 visit 方法

访问者模式的优点

  • 增加新的访问操作很方便
  • 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中
  • 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作

访问者模式的缺点

  • 增加新的元素类很困难
  • 破坏封装

访问者模式的适用场景

  • 一个对象结构包含多种类型的对象,希望对这些对象实施一些依赖其具体类型的操作
  • 需要对一个对象结构中的对象进行很多不同的并且不相关得操作,而且需要避免让这些操作污染这些对象的类,也不希望在增加新操作时修改这些类
  • 对象结构中元素对象对应的类很少改变,但经常需要在此对象结构上定义新的操作