设计模式 -- 11中行为模式

行为模式

行为模式是用于在 算法 和 对象 间分配职责。它们刻画了运行时刻的控制流。

行为类模式:使用继承,在类间分派行为。常见的两个类行为模式为:模板方法 和 翻译者 两个。它们的控制流如下:

  • 模板方法:它先定义一个算法的抽象操作。子类再具体实现这些抽象操作。
  • 翻译者:它将文法表示成语法树,并实现解释器,作为树节点的操作。

行为对象模式:使用组合,来描述各对象如何协作以完成任务。常见的行为对象模式如下所示:

  • 中介者:在多个对等对象间引入中介,以避免每两个对象都要引用对方。(极端情况)
  • 责任链:它将请求提交给一条链。这样就可以隐式的像一个对象发送请求。
  • 观察者:定义并保持对象间的依赖关系。
  • 策略:将算法封装在对象中,并将请求指派给它
  • 命令:将请求封装在对象中,这样就可以传递/存储它了
  • 状态:将某对象状态封装起来,当某对象状态发生变化时,某对象可以改变行为
  • 访问者:封装分布于多个类间的行为
  • 迭代器:封装访问和遍历结合的方法

1.责任链模式

这个模式让你将请求提交给一条链,从而避免了请求发送者和接受者 直接耦合。

链中的处理器,要么处理请求,要么转发到下一个处理器。

要创建责任链模式,可以先创建一个处理器抽象类,这个抽象类持有自己的实例,然后默认操作的是将请求转发给自己持有的这个实例(子类处理器会重新定义这个操作)。它的结构如下所示:
在这里插入图片描述

2.命令模式

Command模式类似过程语言中的回调,先用 Command 接口做个占位,等真正需要发送请求时,再将请求发给处理对象。这样做有如下好处:

  • 可以记录命令,以进行日志,撤销,重做等
  • 解耦请求发送者和处理者

Command模式结构图如下:
在这里插入图片描述

书上举了两个例子:

  • 文本编辑器中 菜单栏 的粘贴命令,它用 Command 模式的结构图如下所示
    在这里插入图片描述

  • 文本编辑器的 菜单栏 的打开文档命令
    在这里插入图片描述

3.解释器模式

解释器是类型为模式。解释器的使用场景 – 当一个语言需要解释执行时。使用解释器的步骤如下:

  • 给语言定义一种文法。
  • 将文法用类表示
  • 将语言的一个句子转成表示类的语法树
  • 给所有表示类定义一个解释操作

这样,待解释语言的一个句子就有了一个解释器。解释器的结构如下所示:
在这里插入图片描述

书上举了一个关于正则表达式的例子,如下所示:

  • 为正则表示定义一种文法:

    expression ::= literal | alternation | sequence | repetition | '{' expression '}'

    alternation ::= expression '|' expression

    sequence ::= expression '&' expression

    repetition ::= expression '*'

    literal ::= 'a' | 'b' | 'c' |... { 'a' | 'b' | 'c' | ...}

  • 将文法用类表示,上面的文法指出:每个句子有四种组成部分:alternation , sequence, repetition, literal ,可以先抽象一个 RegularExpression 抽象类,然后将句子的四种组成部分分别定义成 RegularExpression 的子类。如下所示: 在这里插入图片描述

  • 这样正则表达式的一个句子,就可以转换成上面的表示类树。比如:raining & (dogs | cats) * 对应如下结构: 在这里插入图片描述

  • 给表示类定义了解释操作之后,正则表达式的每个句子就都可以解释执行了。

4.迭代器模式

迭代器模式提供一种方法来访问聚合对象内部元素,而无需暴露聚合对象内部表示。

它通过将 迭代 操作从聚合对象中抽离出来到单独的 Iterator 对象中。这样子做有如下两个好处:

  • 可以并发遍历集合(不修改的情况下),因为遍历的状态不维持在聚合对象中,而是在Iterator中。
  • 实现不同方式的遍历时,只需要添加迭代器,而无需在聚合对象中定义所有可能的遍历方式。

一个列表类使用迭代器的结构如下(聚合对象和迭代器对象都多了一个抽象,且迭代器由聚合对象创建)

注:迭代器对象持有聚合对象
在这里插入图片描述

5.中介者模式

中介者主要用封装对象之间的交互。比如一个系统有11个对象,这些对象间两两都需要通信的话,每个对象需要实现10个方法(持有10个对象)。这些通信方法散落在各对象内,让代码难以理解,一旦某通信方式发生变化,也不好做出变更。因此,可以把这11个对象的通信操作封装在中介者中。这样每个对象只需要引用中介者即可。而中介者集中了所有通信方法。一旦通信方式需要变更,修改起来也方便。

中介者模式的结构图如下所示:
在这里插入图片描述

关于中介者,书上有一个例子比较合适:

即一个GUI界面,有一个列表框,一个输入框。当列表框内容被选中时,输入框显示当前选中的内容。(例子只用了两个组件,但其实还可以更多,比如输入框有内容时,某按钮可用 / 某按钮不可用等)。这种情况下使用中介模式的结构示意图如下:
在这里插入图片描述

它的交互过程如下(注意:同事组件间相互不知道对方存在)
在这里插入图片描述

6.备忘录模式

备忘录模式用于在对象外面保存一个对象的内部状态。借此可以恢复对象到指定状态。

备忘录(memento)是一个对象。它保存另一个对象的状态。后者叫原发器(被保存状态的对象)。它们的工作流程如下:

  • 客户端需要备份原发器数据时,向原发器发起请求
  • 原发器使用自己的状态构造一个备忘录,并将其返回客户端
  • 客户端需要还原原发器状态时,客户端将备忘录发给原发器
  • 原发器根据备忘录内容,重新设置自己的状态

备忘录模式的结构示意图如下所示:(在 Java 中,为了不暴露原发器的内部结构,备忘录是原发器的一个内部类,客户端在外面只看到一个 Object)
在这里插入图片描述

7.观察者模式

观察者模式也叫订阅-发布模式。它用于定义一种一对多的关系。当一个对象的状态发生变化时,依赖于该对象的其他对象都得到通知。

书上举一个例子比较贴切。比如:一个数据对象,它有两种形式进行展示:一是,用表格显示;二是,用柱形图展示。当数据对象发生变化,比如某数据更新时:表格 和 柱形图 都得到通知,从而获取最新数据。这个是很正规的观察者模式。

这个模式的两个关键对象时:目标(subject)和 观察者(observer)。每当目标发生变化时,观察者都会得到通知,然后观察者根据通知做出相应变化。结构图如下:
在这里插入图片描述

上面那个表格和柱状图的例子,它会有两个观察者,一个数据对象。它们的交互流程图可以如下所示:
在这里插入图片描述

8.状态模式

状态模式是一种当对象状态发生变化时,对象的行为也发生变化的模式。(就好像对象的类发生了变化)。这个模式的关键点在于:将对象不同的状态及对应行为封装在一个 State 中

比如,一个 TcpConnection 对象,会根据它的状态是:Established、Listen、Close 而对客户端请求作出不同操作。下面是它的结构图:
在这里插入图片描述

TCPConnection 将请求发生自己持有的 state 进行处理(往往同时把自己也发给state)。TCPConnection 或者 具体 State 都可以切换 TCPConnection 的状态。

9.策略模式

策略模式用来封装算法,使得不同算法之间可以相互替换。一个这样封装算法被叫做策略。

比如一个文本换行类(TextContext),它在正文的不同位置插入换行符。不同时刻,它需如下三种策略之一:

  • Simple策略 – 简单换行策略,每次只找一个位置(插入换行符)
  • TeX策略 – 这个查找换行位置的TEX算法,一次处理一段文字
  • Array策略 – 这个策略使得每行有固定数量的项

如果将这三种算法都分装在 TextContext 中,那耦合太强了,发生变更不容易。因此,可以它们封装在独立 Strategy 中,然后 TextContext 再引用这些 Strategy。它的结构图如下:
在这里插入图片描述

注:策略模式结构图和上面的状态结构图一样:只是所做的事情不同,其的名字不一样而已,其实使用通过类似的结构来完成不同的事情的。这也说明了行为模式是针对具体事情(任务)的

10.模板方法

模板方法用于定义一个算法的骨架,然后将其中一些步骤延迟到子类去实现。即模板方法中的很多步骤使用抽象方法,然后具体的子类就根据上下文实现这些方法。

比如有一个应用(Application)可以打开多种文档(Document),比如 文本/word/pdf 等等。因为不同文档格式不同,所以每类文档都有一个具体的 Document 实现。打开文档的操作可以放在 Application 中,并用抽象的 Document 方法实现这个打开操作,然后具体 Document 子类再实现不同的文档读取方法。这个 Application 中的 open 方法,就是一个模板方法。

它的结构图如下所示:
在这里插入图片描述

11.访问者模式

访问者模式用于抽离类的操作,使得在不修改类的情况下,修改操作。

比如,有一个日志类(Logger)它有时需要将日志打印到终端,有时需要将日志打印到文件。如果直接把这两种操作定义在 Logger 中,那么这个 Logger 很复杂,也难修改(访问者的操作的另一个优势是:这些操作还可以用于其他类)。这时候,可以使用访问者,实现两个 Visitor :ConsoleVisitor 和 FileVisitor。然后在不同需求时,给 Logger 传入不同的 Visitor 即可。

它的结构图如下所示:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值