使用Command Design模式
任何人都可以用命令模式的简单例子来解释。 我在互联网上提到但我感到困惑。
public interface Command { public void execute(); }
在大多数情况下,命令是不可变的,并包含封装按需执行的单个操作的指令。 您可能还有一个RuntimeCommand在执行时接受指令,但这会根据实现深入研究策略或装饰器模式。
在我看来,我认为注意命令的不可变上下文非常重要,否则命令就会成为一个建议。 例如:
public final class StopServerCommand implements Command { private final Server server; public StopServerCommand(Server server) { this.server = server; } public void execute() { if(server.isRunning()) server.stop(); } } public class Application { //... public void someMethod() { stopButton.addActionListener(new ActionListener() { public void actionPerformed(Event e) { stopCommand.execute(); } }); } }
我个人并不喜欢命令。 根据我自己的经验,它们只适用于框架回调。
如果它有所帮助,那就想一下隐喻意义上的命令; 由他/她的指挥官给予训练有素的士兵命令,并且根据要求士兵执行该命令。
以下是您可以使用真实场景来了解命令模式如何工作的另一个示例:您无法使用命令模式通过飞机从一个地方旅行到另一个地方!
如果您经常旅行,那么您作为客户所关心的一切就是从您到达另一个地方旅行。 你不关心飞行员将如何驾驶飞机或哪些航空公司可用……你无法真正预测到这一点。 你想要的只是获得通风口并告诉他们带你到目的地。
但如果你这样做,你对机场当局的命令将被嘲笑! 他们需要你提供一个命令对象,这是你的票。 如果你不关心哪家航空公司或哪种飞机类型,当你准备好飞行时,你需要提供一个票务命令对象。 调用者,机场官员需要检查你的命令(票证),以便他们可以validation它,如果它是假的则撤消它,如果他们犯了错误就重做它(你不需要经过整个预订过程) 。
简而言之,他们希望在决定是否调用或执行您的命令之前完全控制您的命令(票证),从而让航空公司(接收方)执行(将您带到飞机上并带您到达目的地)。
请注意,您的命令(您的机票)已经有接收方(航空公司)的信息,没有这些信息,机场官员甚至不会首先开始处理您的机票。
机场当局甚至可能有一堆他们正在处理的门票。 他们可能会选择推迟我的机票,让跟在我后面的人通过(在我之前调用另一张人的机票)
这是代码:
[TestClass] public class Client { [TestMethod] public void MyFlight_UsingCommandPattern() { var canadianAirline = new Airline(); AirlineTicket_Command myTicket = new MyAirLineTicket(canadianAirline); var airportOfficials = new AirportOfficials_Invoker(myTicket); airportOfficials.ProcessPasengerTicket_And_AllowPassengerToFly_Execute(); //assert not implemented } } public class AirportOfficials_Invoker { private AirlineTicket_Command PassengerTicket { set; get; } public AirportOfficials_Invoker(AirlineTicket_Command passengerTicket) { throw new NotImplementedException(); } public void ProcessPasengerTicket_And_AllowPassengerToFly_Execute() { PassengerTicket.Execute(); } } public abstract class AirlineTicket_Command { protected Airline Airline { set; get; } protected AirlineTicket_Command(Airline airline) { Airline = airline; } public abstract void Execute(); } public class MyAirLineTicket : AirlineTicket_Command { public MyAirLineTicket(Airline airline) : base(airline) { } public override void Execute() { Airline.FlyPassenger_Action(); } } public class Airline { public void FlyPassenger_Action() { //this will contain all those stuffs of getting on the plane and flying you to your destination } }
您可以将Command模式工作流视为如下。
客户端调用Invoker => Invoker调用ConcreteCommand => ConcreteCommand调用Receiver方法,该方法实现抽象的Command方法。
来自dofactory文章的UML图 :
主要特征:
-
Command为所有命令声明一个接口,提供一个简单的execute()方法,该方法要求Receiver执行命令。
-
接收方知道如何执行请求。
-
Invoker拥有一个命令,可以通过调用execute方法让Command执行请求。
-
客户端创建ConcreteCommands并为命令设置Receiver 。
-
ConcreteCommand定义了动作和接收者之间的绑定。
-
当Invoker调用执行时, ConcreteCommand将在Receiver上运行一个或多个操作。
代码段:
interface Command { void execute(); } interface Receiver { public void switchOn(); } class OnCommand implements Command{ private Receiver receiver; public OnCommand(Receiver receiver){ this.receiver = receiver; } public void execute(){ receiver.switchOn(); } } class Invoker { private Command command; public Invoker(Command command){ this.command = command; } public void execute(){ this.command.execute(); } } class TV implements Receiver{ public void switchOn(){ System.out.println("Switch on from TV"); } } class DVDPlayer implements Receiver{ public void switchOn(){ System.out.println("Switch on from DVDPlayer"); } } public class CommandDemoEx{ public static void main(String args[]){ // On command for TV with same invoker Receiver receiver = new TV(); Command onCommand = new OnCommand(receiver); Invoker invoker = new Invoker(onCommand); invoker.execute(); // On command for DVDPlayer with same invoker receiver = new DVDPlayer(); onCommand = new OnCommand(receiver); invoker = new Invoker(onCommand); invoker.execute(); } }
输出:
Switch on from TV Switch on from DVDPlayer
说明:
在这个例子中,
- 命令接口定义了
execute()
方法。 - OnCommand是ConcreteCommand ,它实现了
execute()
方法。 - Receiver是一个接口,实现者必须为这些方法提供实现。
- TV和DVDPlayer是两种类型的接收器 ,它们像OnCommand一样传递给ConcreteCommand。
- Invoker包含命令 。 这是将Sender与Receiver分离的关键。
- Invoker接收OnCommand – >,它调用Receiver (TV)来执行此命令。
通过使用Invoker,您可以打开电视和DVDPlayer。 如果您扩展此程序,您也关闭电视和DVDPlayer。
您可以使用命令模式
-
解耦命令的发送者和接收者
-
实现回调机制
-
实现撤消和重做function
-
保持命令的历史
看看这个dzone和journaldev以及维基百科的文章。
维基百科页面的源代码简单,清晰,自我解释。
如果您按照本文中引用的步骤操作,则可以实现撤消和重做
我的要求是执行一系列任务(可以在几个Usecases中重用),每个任务都有自己的exception流程。 在这里找到Command模式的实现逻辑。
我试图让它像命令执行的每个动作(无论正常/备用流程)也可以是exception处理程序。 但是,如果该命令是使用其他处理程序注册的,则应使用此命令。 欢迎任何改进/纠正的建议。
public interface Command { Result run() throws Exception; Command onException(ExceptionHandler handler); } public class Result { } public interface ExceptionHandler { void handleException(Exception e); } public interface Action { Result execute() throws Exception; } public class BasicCommand implements Command { private Action action; private ExceptionHandler handler; public BasicCommand(Action action) { if (action == null) { throw new IllegalArgumentException("Action must not be null."); } this.action = action; this.handler = (ExceptionHandler) this.action; } @Override public Command onException(ExceptionHandler handler) { if (handler != null) { this.handler = handler; } return this; } public Result run() throws Exception { Result result = null; try { result = action.execute(); } catch (Exception e) { handler.handleException(e); } return result; }
}
public class BasicAction implements Action, ExceptionHandler { private Object[] params; public BasicAction(Object... params) { this.params = params; } @Override public Result execute() throws Exception { // TODO Auto-generated method stub return null; } @Override public void handleException(Exception e) { // TODO exception translation: prepare unchecked application. exception and throw.. } } public class Main { public static void main(String[] args) throws Exception { int param1 = 10; String param2 = "hello"; // command will use the action itself as an exception handler Result result = new BasicCommand(new BasicAction(param1, param2)).run(); ExceptionHandler myHandler = new ExceptionHandler(){ @Override public void handleException(Exception e) { System.out.println("handled by external handler"); } }; // command with an exception handler passed from outside. Result result2 = new BasicCommand(new BasicAction(param1, param2)).onException(myHandler).run(); } }
命令设计模式将服务调用者和服务提供者分离。 在一般场景中,例如,如果Object A
想要Object B
服务,它将直接调用B.requiredService()
。 因此,A知道B.在命令模式中,删除了这种耦合。 在这里,有一个称为Command
的中间对象,它进入了画面。 因此, A
处理Command
对象和命令对象处理实际对象B
这种方法有几个应用程序,如设计应用程序,它们是:
- 接受命令作为请求。
- 撤消请求。
- 请求请求。
- 创建宏。
- 创建任务执行程序和任务管理器。
有关Command Design Pattern的更多信息,我建议您使用https://en.wikipedia.org/wiki/Command_pattern 。 有关所有其他设计模式,请参阅https://www.u-cursos.cl/usuario/…/mi_blog/r/head_first_design_patterns.pdf