谁能用一个简单的例子来解释命令模式?我尝试在互联网上搜索,但我很困惑。
6 回答
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();
}
});
}
}
我个人不太喜欢命令。根据我自己的经验,它们只适用于框架回调。
如果有帮助,请以隐喻的方式思考命令;受过训练的士兵由他/她的指挥官发出命令,并根据要求士兵执行该命令。
您可以将命令模式工作流程视为如下。
客户端调用Invoker => Invoker调用ConcreteCommand => ConcreteCommand调用Receiver方法,它实现了抽象的Command方法。
来自dofactory文章的UML图:
主要特征:
Command为所有命令声明了一个接口,提供了一个简单的 execute() 方法,该方法要求命令的接收者执行操作。
接收方知道如何执行请求。
Invoker持有一个命令,可以通过调用 execute 方法获取该命令执行请求。
客户端创建具体命令并为命令设置接收器。
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是两种类型的Receivers,它们像 OnCommand 一样传递给 ConcreteCommand。
- Invoker包含Command。这是将 Sender 与Receiver分离的关键。
- Invoker接收OnCommand -> 调用Receiver (TV) 来执行此命令。
通过使用 Invoker,您可以打开 TV 和 DVDPlayer。如果你扩展这个程序,你也关闭了 TV 和 DVDPlayer。
您可以使用命令模式
解耦命令的发送者和接收者
实现回调机制
实现撤消和重做功能
维护命令历史
看看这个dzone和journaldev以及Wikipedia文章。
作为 Wikipedia 页面的源代码简单、干净且不言自明。
如果您按照本文中引用的步骤进行操作,则可以实施撤消和重做
这是另一个示例,您可以使用现实生活场景来了解命令模式的工作原理:如果不使用命令模式,您将无法乘飞机从一个地方旅行到另一个地方!
如果您经常旅行,作为客户,您所关心的只是从您所在的地方旅行到另一个地方。您不关心飞行员将如何驾驶飞机或哪家航空公司可用..您无法真正预测到这一点。你想要的只是去机场并告诉他们带你去你的目的地。
但如果你这样做,你对机场当局的指挥会被嘲笑!他们需要你提供一个命令对象,这是你的票。尽管您不关心哪家航空公司或哪种飞机类型,但当您准备好飞行时,您需要提供票务命令对象。调用者,即机场官员需要检查您的命令(机票),以便他们可以验证它,如果它是假的则撤消它,如果他们犯了错误则重做它(您不必重新完成预订过程) .
简而言之,他们希望在决定是否调用或执行您的命令之前完全控制您的命令(票证),这让航空公司(接收者)执行(让您登上飞机并带您前往目的地)。
请注意,您的命令(您的机票)已经拥有接收者(航空公司)的信息,没有这些信息,机场官员甚至不会首先开始处理您的机票。
机场当局甚至可能有一堆他们正在处理的机票。他们可能会选择延迟我的票并让在我之后的人通过(在我之前调用另一个人的票)
这是代码:
[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
}
}
我的要求是执行一系列任务(可以在多个用例中重复使用),每个任务都有自己的异常流。在这里找到了命令模式的实现逻辑。
我试图使它像命令执行的每个操作(无论是正常/备用流程)也可以是异常处理程序。但是,如果该命令已注册到另一个处理程序,那么应该使用它。欢迎任何改进/纠正的建议。
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();
}
}
我想在这里给你另一个粗略的类比。
假设有一天上帝呼唤你,告诉你世界处于危险之中,他需要你的帮助来拯救它。进一步帮助你,他告诉你他已经在地球上派了一些超级英雄。
由于他不知道哎呀,因此他不称他们为超级英雄(不为您提供任何接口或抽象类),而只是告诉您他们的前任蝙蝠侠,超人,钢铁侠的名字以及他们拥有的力量.
他还说,将来他可能会派出更多这样的人。
现在他分配给你特殊的责任->控制它们,并为你提供了七只手。他不会自己解决每只手的任务,而是把它留给你。
您希望灵活地分配任何超级英雄力量的任何手动控制,并且不希望通过多种条件反复改变事物。
你正在修复中。你现在做什么?
输入命令模式。
创建一个接口 Command ,其中只有一个方法 execute() 。封装每个超级英雄的每一个力量,并为前任执行命令 - IronManCreatesSuitCommand
现在,您可以随时将任何手分配给任何命令,从而为您提供更大的灵活性,因为现在您的任何一只手都不关心它必须执行的特定任务。您只需为其分配任何命令即可。它调用它执行,该命令负责其他所有事情。
现在,即使上帝派了任何其他具有不同力量的超级英雄,你也知道该怎么做。
命令设计模式将服务调用者和服务提供者解耦。在一般情况下,例如,如果Object A
想要服务Object B
,它将直接调用B.requiredService()
。因此,A 知道 B。在命令模式中,这种耦合被移除。在这里,有一个称为 的中间对象Command
,它出现在图片中。因此,A
处理Command
对象和命令对象处理实际对象B
。这种方法有几个应用程序,例如设计应用程序,它们是:-
- 接受命令作为请求。
- 撤消请求。
- 请求请求。
- 创建宏。
- 创建任务执行器和任务管理器。
有关命令设计模式的更多信息,我会推荐https://en.wikipedia.org/wiki/Command_pattern。对于所有其他设计模式,请参阅https://www.u-cursos.cl/usuario/.../mi_blog/r/head_first_design_patterns.pdf