命令模式(Command Pattern)【使用频率:★★★★☆】

1. 概述

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

命令模式的本质就在于将命令进行封装,将发出命令的责任和执行命令的责任分开。

2. 模式中的角色

  2.1 Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。

  2.2 ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。

  2.3 Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。

2.4 Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。

3. 模式解读

  3.1 模式的类图

C#设计模式读书笔记之命令模式(Command Pattern)_命令模式

       3.2 代码实现

       这里以电视机为例。电视剧是请求的接受者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应着不同的操作。在这里遥控器需要执行三个命令:打开电视机、关闭电视机、换台。

using System;

namespace ConsoleApp2
{
class Class12
{
public static void Main(string[] args)
{
ICommand openCommand, closeCommand, changeCommand;

openCommand = new OpenTvCommand();
closeCommand = new CloseTvCommand();
changeCommand = new ChangeChannelCommand();

Invoker invoker = new Invoker(openCommand, closeCommand, changeCommand);

invoker.Open(); //打开电视机
invoker.ChangeChannel(); //换频道
invoker.ChangeChannel();
invoker.ChannelUndo();
invoker.ChannelUndo();
invoker.ChannelUndo();
invoker.Close(); //关闭电视机

Console.ReadLine();
}
}

// 抽象命令接口
public interface ICommand
{
void Execute(int i);
}

// 具体命令类
public class OpenTvCommand : ICommand
{
private Television m_Tv;

public OpenTvCommand()
{
m_Tv = new Television();
}

public void Execute(int i)
{
m_Tv.Open();
}
}

// 具体命令类
public class ChangeChannelCommand : ICommand
{
private Television m_Tv;

public ChangeChannelCommand()
{
m_Tv = new Television();
}

public void Execute(int i)
{
m_Tv.ChangeChannel(i);
}
}

// 具体命令类
public class CloseTvCommand : ICommand
{
private Television m_Tv;

public CloseTvCommand()
{
m_Tv = new Television();
}

public void Execute(int i)
{
m_Tv.Close();
}
}

// 接收者
public class Television
{
public void Open()
{
Console.WriteLine("打开电视机......");
}

public void Close()
{
Console.WriteLine("关闭电视机......");
}

public void ChangeChannel(int i)
{
Console.WriteLine("切换电视频道......现在是频道" + i);
}
}

// 调用者
public class Invoker
{
private ICommand m_OpenTVCommand;
private ICommand m_CloseTVCommand;
private ICommand m_ChangeChannelCommand;

public int m_NowChannel = 0; //当前频道
public int m_PreviousChannel; //前一个频道,用于执行返回操作

public Invoker(ICommand openTvCommand, ICommand closeTvCommand, ICommand changeChannelCommand)
{
m_OpenTVCommand = openTvCommand;
m_CloseTVCommand = closeTvCommand;
m_ChangeChannelCommand = changeChannelCommand;
}

// 打开电视剧
public void Open()
{
m_OpenTVCommand.Execute(0);
}

// 关闭电视机
public void Close()
{
m_CloseTVCommand.Execute(0);
}

// 换频道
public void ChangeChannel()
{
// 换频道前记录当前频道
m_PreviousChannel = m_NowChannel;
// 频道+1
m_NowChannel++;
m_ChangeChannelCommand.Execute(m_NowChannel);
}

// 频道返回
public void ChannelUndo()
{
// 将以前的频道传入
m_ChangeChannelCommand.Execute(m_PreviousChannel);
// 当前频道与前一个频道进行互换
int tempChannel;
tempChannel = m_PreviousChannel;
m_PreviousChannel = m_NowChannel;
m_NowChannel = tempChannel;
}
}
}

4、模式优缺点

4.1 优点

  • 降低了系统耦合度
  • 新的命令可以很容易添加到系统中去。

4.2 缺点

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

5、模式使用场景

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