使用Command Design模式

时间:2010-01-06 19:23:56

标签: java oop design-patterns command-pattern

任何人都可以用命令模式的简单例子来解释。我在互联网上提到但是我很困惑。

6 个答案:

答案 0 :(得分:20)

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();
            }
        });
    }
}

我个人并不喜欢命令。根据我自己的经验,它们只适用于框架回调。

如果它有所帮助,可以用一种隐喻的方式来思考一个命令;训练有素的士兵由他/她的指挥官发出命令,并按要求士兵执行此命令。

答案 1 :(得分:7)

您可以将Command模式工作流视为如下。

客户端调用 Invoker => Invoker 调用 ConcreteCommand => ConcreteCommand 调用 Receiver 方法,该方法实现了抽象的 Command 方法。

来自dofactory文章的

UML图

enter image description here

主要功能:

  1. 命令为所有命令声明一个接口,提供一个简单的execute()方法,要求接收方执行操作。

  2. 接收方了解如何执行请求。

  3. Invoker 拥有一个命令,可以通过调用execute方法让命令执行请求。

  4. 客户端创建 ConcreteCommands 并为该命令设置 Receiver

  5. ConcreteCommand 定义了操作和接收者之间的绑定。

  6. Invoker 调用执行 ConcreteCommand 时,将在Receiver上运行一个或多个操作。

  7. 代码段:

    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
    

    说明:

    在此示例中,

    1. 命令接口定义了execute()方法。
    2. OnCommand ConcreteCommand ,它实现了execute()方法。
    3. Receiver 是一个接口,实施者必须为这些方法提供实现。
    4. 电视 DVDPlayer 是两种类型的接收器,它们会像OnCommand一样传递给ConcreteCommand。
    5. 邀请者包含命令。这是将发件人与接收器分离的关键。
    6. 邀请者接收 OnCommand - >它会调用 Receiver (TV)来执行此命令。
    7. 通过使用Invoker,您可以打开电视和DVDPlayer。如果你扩展这个程序,你也关闭了电视和DVDPlayer。

      您可以使用命令模式

      1. 将发件人和发件人联系起来命令接收者

      2. 实施回调机制

      3. 实施撤消和重做功能

      4. 维护命令历史

      5. 查看此dzone以及journaldevWikipedia条款。

        维基百科页面的源代码简单,清晰,自我解释。

        如果您按照此article

        中引用的步骤操作,则可以实施撤消重做

答案 2 :(得分:6)

以下是您可以使用真实场景来了解命令模式如何工作的另一个示例:您无法使用命令模式通过飞机从一个地方旅行到另一个地方!

如果您经常旅行,那么您作为客户所关心的一切就是从您到达另一个地方旅行。你不关心飞行员将如何驾驶飞机或哪些航空公司可用......你无法真正预测到这一点。你想要的只是获得通风口并告诉他们带你到目的地。

但如果你这样做,你对机场当局的命令将被嘲笑!他们需要你提供一个命令对象,这是你的票。尽管你不关心哪家航空公司或哪种飞机类型,但是当你准备好飞行时,你需要提供一个票务命令对象。调用者,机场官员需要检查你的命令(票证),以便他们可以验证它,如果它是假的则撤消它,如果他们犯了错误就重做它(你不必经过整个预订过程) 。

简而言之,他们希望在决定是否调用或执行命令之前完全控制您的命令(票证),这样可以让航空公司(接收方)执行(让您乘坐飞机并带您到您的目的地)。

请注意,您的命令(您的机票)已经有接收方(航空公司)的信息,没有这些信息,机场官员甚至不会首先开始处理您的机票。

机场当局甚至可能会收到一堆他们正在处理的门票。他们可能会选择延迟我的机票,让跟在我后面的人通过(在我之前调用另一张人的票)

以下是代码:

 [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
        }
    }

答案 3 :(得分:3)

我的要求是执行一系列任务(可以在几个Usecases中重复使用),每个任务都有自己的异常流程。在这里找到命令模式的实现逻辑。

我试图让它像命令执行的每个动作(无论正常/备用流程)也可以是异常处理程序。但是,如果该命令是使用其他处理程序注册的,则应使用此命令。欢迎任何改进/纠正的建议。

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();

    }
}

答案 4 :(得分:0)

命令设计模式将服务调用者和服务提供者分离。在一般情况下,例如,如果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

答案 5 :(得分:0)

我会在这里再给你一个粗略的比喻。

假设有一天上帝呼唤您,并告诉您世界处于危险之中,他需要您的帮助来拯救它。他进一步帮助您,他告诉您他已经在地球上派出了一些超级英雄。

由于他不知道哎呀,因此他不称呼他们为超级英雄(没有为您提供任何接口或抽象类),而只是告诉您他们的名字-前蝙蝠侠,超人,​​钢铁侠以及他们拥有的力量。

他还说,将来他可能会派出更多这样的人。

现在,他为您分配了特殊责任-> 控制它们,为此,您将拥有七只手。他不会自己解决每个任务的任务,而是将任务留给您。

您希望灵活地分配任何超级英雄的权限,而又不想在多个条件下重复更改。

您已解决。你现在做什么?

输入命令模式。

创建一个接口Command,并且其中只有一个方法execute()。 封装每个超级英雄的所有力量,并使该实现Command for ex-IronManCreatesSuitCommand

现在您可以随时将任意一只手分配给任何命令,从而为您提供了更大的灵活性,因为现在您的一只手都不在乎它必须执行的特定任务。您只需为其分配任何命令即可。它会在其上调用execute,而命令会处理其他所有内容。

现在,即使上帝派遣其他具有不同能力的超级英雄,您也知道该怎么做。