Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么我应该使用命令设计模式,而我可以轻松调用所需的方法?_Java_Oop_Design Patterns_Command Pattern - Fatal编程技术网

Java 为什么我应该使用命令设计模式,而我可以轻松调用所需的方法?

Java 为什么我应该使用命令设计模式,而我可以轻松调用所需的方法?,java,oop,design-patterns,command-pattern,Java,Oop,Design Patterns,Command Pattern,我正在学习,我对它的使用方式感到困惑。我的示例与一个用于打开和关闭灯光的远程控制类有关 为什么我不应该使用Light类的switchOn()/switchOff()方法,而不是使用单独的类和方法来最终调用switchOn/switchOff方法 我知道我的例子很简单,但这就是重点。我在互联网上的任何地方都找不到任何复杂的问题来查看命令设计模式的确切用法。 如果您知道使用此设计模式可以解决的任何复杂现实问题,请与我分享。这有助于我和这篇文章的未来读者更好地理解这种设计模式的用法。谢谢 //Comm

我正在学习,我对它的使用方式感到困惑。我的示例与一个用于打开和关闭灯光的远程控制类有关

为什么我不应该使用Light类的switchOn()/switchOff()方法,而不是使用单独的类和方法来最终调用switchOn/switchOff方法

我知道我的例子很简单,但这就是重点。我在互联网上的任何地方都找不到任何复杂的问题来查看命令设计模式的确切用法。

如果您知道使用此设计模式可以解决的任何复杂现实问题,请与我分享。这有助于我和这篇文章的未来读者更好地理解这种设计模式的用法。谢谢

//Command
public interface Command {
  public void execute();
}

//Concrete Command
public class LightOnCommand implements Command {

  //Reference to the light
  Light light;

  public LightOnCommand(Light light) {
    this.light = light;
  }

  public void execute() {
    light.switchOn();        //Explicit call of selected class's method
  }
}

//Concrete Command
public class LightOffCommand implements Command {

  //Reference to the light
  Light light;

  public LightOffCommand(Light light) {
    this.light = light;
  }

  public void execute() {
    light.switchOff();
  }
}

//Receiver
public class Light {
  private boolean on;

  public void switchOn() {
    on = true;
  }

  public void switchOff() {
    on = false;
  }
}

//Invoker
public class RemoteControl {
  private Command command;

  public void setCommand(Command command) {
    this.command = command;
  }

  public void pressButton() {
    command.execute();
  }
}

//Client
public class Client {
  public static void main(String[] args) {
    RemoteControl control = new RemoteControl();
    Light light = new Light();
    Command lightsOn = new LightsOnCommand(light);
    Command lightsOff = new LightsOffCommand(light);

    //Switch on
    control.setCommand(lightsOn);
    control.pressButton();

    //Switch off
    control.setCommand(lightsOff);
    control.pressButton();
  }
}
为什么我不能轻松地使用以下代码?

 Light light = new Light();
 switch(light.command) {
  case 1:
    light.switchOn();
    break;
  case 2:
    light.switchOff();
    break;
 }

你不必这么做。设计模式只是过去一些人在编写非常复杂的应用程序时发现有用的指导原则

在你的情况下,如果你要做的是打开和关闭电灯开关,而不是其他什么,那么第二个选择是不需要动脑筋的


更少的代码几乎总是比更多的代码好。

使用命令模式的主要动机是命令的执行者根本不需要知道命令是什么、它需要什么上下文信息或它做什么。所有这些都封装在命令中

这允许你做一些事情,比如有一个按顺序执行的命令列表,它依赖于其他项目,分配给一些触发事件等等

在您的示例中,您可以有其他类(例如,
空调
),它们有自己的命令(例如,
打开恒温器
关闭恒温器
)。这些命令中的任何一个都可以分配给按钮,或者在满足某些条件时触发,而不需要任何命令知识

因此,总之,该模式封装了执行操作所需的所有内容,并允许操作的执行完全独立于任何上下文。如果这不是您的要求,那么该模式可能对您的问题空间没有帮助

下面是一个简单的用例:

interface Command {
    void execute();
}

class Light {
    public Command turnOn();
    public Command turnOff();
}

class AirConditioner {
    public Command setThermostat(Temperature temperature);
}

class Button {
    public Button(String text, Command onPush);
}

class Scheduler {
    public void addScheduledCommand(Time timeToExecute, Command command);
}
然后,您可以执行以下操作:

new Button("Turn on light", light.turnOn());
scheduler.addScheduledCommand(new Time("15:12:07"), airCon.setThermostat(27));
scheduler.addScheduledCommand(new Time("15:13:02"), light.turnOff());
正如您所看到的,
按钮
调度程序
根本不需要了解有关命令的任何信息<代码>调度程序是可能包含命令集合的类的一个示例

还请注意,在Java 8中,函数接口和方法引用使此类代码更加整洁:

@FunctionalInterface
interface Command {
    void execute();
}

public Light {
    public void turnOn();
}

new Button("Turn On Light", light::turnOn);   

现在,转化为命令的方法甚至不需要了解命令-只要它们具有正确的签名,就可以通过引用该方法安静地创建匿名命令对象

可能性很多,但通常是:

  • 构建一个命令行框架,从操作中抽象出选项的解析。然后,您可以使用类似于
    opts.register(“--on”,new LightOnCommand())
    的内容注册操作
  • 允许用户拖放一系列操作以作为宏执行
  • 在触发某些事件时注册回调,例如(event.ENTER\u ROOM,new LightOnCommand())
这里的一般模式是,您有一段代码负责在不知道某个操作是什么的情况下确定需要执行某个操作,另一段代码知道如何执行某个操作,但不知道何时执行


例如,在第一个示例中,
opts
实例知道,当它看到命令行选项--on时,应该打开指示灯。但它知道这一点,却不知道“开灯”是什么意思。事实上,opts实例很可能来自第三方库,因此它无法了解灯光。它所知道的只是如何将操作(命令)与它解析的命令行选项关联起来。

您给出的示例
ICommand
相当有限,仅在没有lambda表达式的编程语言中真正有用。Sprinter在他的答案中很好地说明了这一点,说明了命令工厂的使用

命令模式的大多数情况包括其他方法,例如,
CanRun
和/或
Undo
。这些允许按钮根据命令的状态更新其启用状态,或允许应用程序实现撤消堆栈


与大多数设计模式一样,命令模式也有其自身的特点,使事情变得更加复杂。这也是众所周知的,因此有助于让大多数程序员清楚地了解您的代码。

让我们关注命令设计的非实现方面,以及使用命令设计模式的一些主要原因,这些原因分为两大类:

  • 隐藏命令执行方式的实际实现
  • 允许围绕命令(也称为命令扩展)构建方法
隐藏实现 在大多数编程中,您都希望隐藏实现,以便在查看最重要的问题时,它由可理解的命令/代码子集组成。也就是说,你不需要/不想知道灯是如何打开的,或者汽车是如何启动的。如果你的重点是启动汽车,你不需要了解发动机是如何工作的,进入发动机需要多少燃料,气门是如何工作的

指示动作,而不是如何完成 命令会提供这种视图。您将立即了解
TurnLightOn
命令的作用,或
StartCar
。使用命令,您将隐藏如何完成某项操作的细节,同时清楚地指示要执行的操作