行为模式#
本篇包含剩余未介绍到的行为模式:
- 状态模式(State)
- 策略模式 (Strategy)
- 模板方法 (Template Method)
- 访问者模式 (Visitor)
状态模式#
状态模式能够在一个对象的内部状态变化时更改其行为,使其看上去像改变了自身所属的类一样。
状态模式的核心思想主要依托于有限状态机:
程序在任意时刻都仅仅可以处在几种「有限的状态」中,在任何一个特定状态,程序的行为都不相同,并且可以瞬间从一个状态切换到另一个状态。根据当前状态,程序可以选择切换到另一种状态也可以选择保持状态不变。
有限状态机通常使用许多条件运算符实现 if
或 switch
。
通过我们的经验和敏锐的嗅觉,if
等条件运算符在项目结构规模逐渐壮大起来后,是一个定时炸弹。我们不会想在我们的程序中看到全是 if
条件语句的情况。
解决办法#
状态模式建议将对象的所有可能状态新建一个类 State
,然后将所有状态的对应行为抽取到这些类中。
原始对象为 Context 它不会自行实现所有行为,而是保存一个指向表示当前状态的状态对象的引用,便把所有和状态相关的工作直接委派给该对象。
如果 Context 需要转换到另外一种状态,需要将当前活动的状态对象替换为另一个代表新状态的对象(所有状态类遵循同样的接口)Context 通过接口和这些对象进行交互。
这样的结构看起来和策略模式非常相似,但在状态模式中特定状态知道其他状态的存在,能够触发一个状态到另一个状态的转变,策略模式完全不知道其他策略的存在
总结#
应用场景:
- 如果某个类需要根据成员变量的当前值更改自身行为,需要大量的条件语句时
- 如果对象需要根据自身当前状态进行不同行为,同时状态数量多且与状态相关的代码会频繁变动时
桥接模式, 策略模式, 状态模式, 的接口都非常相似,事实上他们都是基于组合模式的思想: 都是将工作委派给其他对象
策略模式#
策略模式能让我们定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。
主要思想#
和状态模式相同,名为 Context 的原型类必须包含一个成员变量来存储对于每种策略的引用,上下文并不执行任务,而是将工作委派给已经连接的策略对象。
原型类不负责选择符合任务需要的算法,客户端将所需要的策略传递给 Context,实际上 Context 并不了解具体的策略,只是通过同样的通用接口和所有策略进行交互。
总结#
- 当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
- 当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
- 我们可以利用策略实现:组合来替代继承
- 模板方法模式基于继承机制:它允许你通过扩展子类中的部分内容来改变部分算法 策略基于组合机制:你可以通过对相应行为提供不同的策略来改变对象的部分行为。模板方法在类层次上运作,因此它是静态的。策略在对象层次上运作,因此允许在运行时切换行为。
How To Implement#
- 从上下文类中找出修改频率较高的算法 (也可能是用于在运行时选择某个算法变体的复杂条件运算符)。
- 声明该算法所有变体的通用策略接口。
- 将算法逐一抽取到各自的类中, 它们都必须实现策略接口。
- 在上下文类中添加一个成员变量用于保存对于策略对象的引用。 然后提供设置器以修改该成员变量。 上下文仅可通过策略接口同策略对象进行交互, 如有需要还可定义一个接口来让策略访问其数据。
- 客户端必须将上下文类与相应策略进行关联, 使上下文可以预期的方式完成其主要工作。
模板方法模式#
模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
主要思想#
在类似于提供读取并处理文件数据的程序场景中,文件的格式可以从 Doc、Excel、CSV 不等(这样我们就会创造三个相对应的方法类)但是这三个类中会包含许多相似的代码,在数据处理和分析的代码会几乎完全一样,差异仅仅在于读取文件的步骤。代码重复性提高了很多。
模板方法模式则建议将算法分解为一系列的步骤,之后将这些步骤抽象为对应的方法,最后在「模板方法」中依次调用这些方法。步骤可以是「抽象」,的也可以提供默认实现,为了能够使用算法,客户端需要继承模板方法类,并根据自己的需求重写相对应的步骤方法,以及实现所有的抽象步骤方法。
总结#
应用场景:
- 当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。
- 多个类的算法除一些细微不同之外几乎完全一样时,你可使用该模式。但其后果是,只要算法发生变化,你就可能需要修改所有的类。
工厂方法模式是模板方法模式的一种特殊形式。