设计模式 行为型设计模式
行为型模式关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信与协作,明确对象的职责。
一、职责链模式
二、命令模式
请求发送者与接收者解耦
1. 自定义功能键
有一个软件,提供了一系列的自定义功能按键,用户可通过这些功能按键来实现一些快捷操作。
例,欲使用以下代码实现功能键与功能处理类之间的调用关系:
/// <summary>
/// 功能键类,请求发送者
/// </summary>
class FunctionButton
{
private HelperHandler help;
/// <summary>
/// 在FunctionButton的OnClick方法中调用HelpHandler的display方法
/// </summary>
public void OnClick()
{
help = new HelperHandler();
help.Display();
}
}
功能键FunctionButton充当请求的发送者,帮助文档处理类HelpHandler充当请求的接收者,在发送者FunctionButton的OnClick方法中调用接收者HelpHandler的Display方法。
以上代码的3个问题:
(1)发送者和接收者之间的方法直接调用,耦合度高。更换请求接收者,必须更换修改发送者的源代码。例如,若需要将接收者HelpHandler改为WindowHandler,需要修改FunctionButton的代码。违背了开闭原则。
(2)FunctionButton类在设计和实现时功能已被固定,如果增加一个新的请求接收者,如果不修改原有的FunctionButton类,则必须增加一个新的与FunctionButton功能类似的类,导致类的个数增加。
(3)用户无法按照自己的需要来设置某个功能键的功能。一个功能键类的功能一旦固定,不修改源代码请情况下,无法更换功能,缺乏灵活性。
2. 命令模式概述
软件开发中,经常需要向某些类发送请求(调用其中的某个方法),但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个。
命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。
命令模式定义:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。别名:动作模式;事务模式
命令模式的核心在于引入命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需要指定一个命令对象,再通过命令对象来调用请求接收者的处理方法,结构如下图:
命令模式包含4个角色:
(1)抽象命令类(Command):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的Execute等方法,通过这些方法可以调用请求接收者的相关操作。
(2)具体命令类(ConcreteCommand):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定到其中。在实现Execute方法时,将调用接收者对象的相关操作。
(3)调用者(Invoker):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的Execute方法,从而实现间接调用请求接收者的相关操作。
(4)接收者(Receiver):接收者执行与请求相关的操作,它具体实现对请求的业务处理。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。
命令模式关键在于引入抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。在最简单的抽象命令类中只包含了一个抽象的Execute方法,每个具体命令类将一个Receiver类型的对象作为一个实例变量进行存储,从而具体指定一个请求的接收者,不同的具体命令提供了Execute方法的不同实现,并调用不同接收者的请求处理方法。
典型的抽象命令类:
abstract class Command
{
public abstract void Execute();
}
对于请求发送者(调用者)而言,将针对抽象命令类进行编程,可以通过构造注入或者设值注入的方式在运行时传入具体命令类对象,并在业务方法中调用命令对象的Execute方法: