前置文章: 设计模式的原则
其他设计模式:用心理解设计模式
设计模式相关代码已统一放至 我的 Github
一、定义
行为型模式之一。
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
(将请求封装为对象,从而让你用不同的请求、队列、日志请求,将客户端参数化,并且支持撤销操作)
二、结构解析
命令模式的一般结构有四种角色:抽象命令,具体命令,实现者,调用者。或者也可认为有五个角色,多一个客户(装配者)。
抽象命令(Command):负责定义一个命令执行的接口方法。
具体命令(ConcreteCommand):关联/持有实现者,并在实现的命令执行接口方法中,对实现者提供的功能接口进行组织,形成具体命令。
实现者(Implementor):或叫做 接收者(Receiver),它是命令的实际执行者。只做自己的事,暴露出自己能提供的功能接口,不关心命令。
调用者(Invoker):使用命令对象的入口,让命令开始执行,可能聚合/持有了N条命令。只关心发出怎样的命令。
三、评价
命令模式解决了调用者在产生新命令时,都得修改实现者(实现者在内部根据输入命令的不同做不同处理)的问题。
它的核心思想是 “调用者在接受命令后,反调命令自备处理方法”。请参考 消除程序中的 if else(二),实际就是使用了 策略模式。
“命令” 中的执行方法就是策略,策略由实现者提供的功能组织而成。
命令新增时实现者将保持不变(除非它提供的功能方法不能通过一定组织实现新的命令)。符合符合开闭原则。
四、额外思考
Unity 的 事件系统/消息系统 就是使用的命令模式。
1、定义抽象实现者,定义实现者可提供的功能接口,(需要继承 IEventSystemHandler )。
using UnityEngine.EventSystems;
public interface IDoSth : IEventSystemHandler
{
//接收到消息/命令时,可执行的事情。只关心自己能做什么
void DoSth1();
void DoSth2();
}
2、定义具体实现者,实现1中定义的接口。(需要继承MonoBehaviour)
public class Implementor : MonoBehaviour, IDoSth
{
public void DoSth1() { Debug.Log("DoSth1"); }
public void DoSth2() { Debug.Log("DoSth2"); }
}
3、定义调用者,装配命令(下面 Lambda表达式中,对实现者提供的功能接口进行组织,形成具体命令),并执行。
public class Invoker : MonoBehaviour
{
private void Start()
{
ExecuteEvents.Execute<IDoSth>(this.gameObject, null, (t, eventData)=> {
t.DoSth1();
t.DoSth2();
});
}
}
注意:此例中的 命令对象,实际就是这个Lambda表达式, 其类型(委托) 为 UnityEngine.EventSystems.ExecuteEvents.EventFunction, 该委托的参数1是实现者对象,参数2是额外的事件执行参数。
五、实现
namespace Commond
{
//命令实现者,做自己的事,不关心命令
public class Implementor
{
public void DoSthX() { }
public void DoSthY() { }
public void DoSthZ() { }
}
//抽象命令
public abstract class Command
{
public abstract void Execute();
}
//具体命令A
public class ConcreteCommandA : Command
{
//持有实现者
private Implementor implementor;
public ConcreteCommandA(Implementor implementor)
{
this.implementor = implementor;
}
public override void Execute()
{
//组织实现者的功能,实现具体命令
//例如这里要使用实现者的XY功能方法
this.implementor.DoSthX();
this.implementor.DoSthY();
}
}
//具体命令B
public class ConcreteCommandB : Command
{
private Implementor implementor;
public ConcreteCommandB(Implementor implementor)
{
this.implementor = implementor;
}
public override void Execute()
{
//组织实现者的功能,实现具体命令
//例如这里要使用实现者的Z功能方法
this.implementor.DoSthZ();
}
}
//命令调用者
public class Invoker
{
//聚合持有命令,这里简化为参数方式传入
public void Invoke(Command command)
{
command.Execute();
}
}
//客户,命令装配
public class Client
{
public Client()
{
//构造命令实现者
Implementor implementor = new Implementor();
//装配具体命令, 将命令实现者与具体命令关联
ConcreteCommandA concreteCommandA = new ConcreteCommandA(implementor);
ConcreteCommandB concreteCommandB = new ConcreteCommandB(implementor);
//构造命令调用者
Invoker invoker = new Invoker();
//命令作为参数,提供给命令调用者
invoker.Invoke(concreteCommandA);
invoker.Invoke(concreteCommandB);
}
}
}
五、撤销操作
TODO