原文: https://howtodoinjava.com/design-patterns/behavioral/command-pattern/

命令模式是一种行为型设计模式,可用于将业务逻辑抽象为离散的动作,我们将其称为命令。 此命令对象有助于松散耦合两个类之间的关系,其中一个类(调用者)应调用另一类(接收者)上的方法来执行业务操作。

让我们学习命令如何帮助将调用者与接收者解耦。

  1. Table of Contents
  2. Introduction
  3. Design Participants
  4. Problem Statement
  5. Command Pattern Implementation
  6. Demo
  7. When to Use Command Pattern
  8. Popular Implementations
  9. Summary

介绍

在面向对象的编程中,命令模式是一种行为型设计模式,其中一个对象用于封装执行动作,业务操作或触发事件所需的所有信息。 方法名称,接收方对象引用和方法参数值(如果有)。 该对象称为命令

类似的方法也适用于责任链模式。 唯一的区别是命令中只有一个请求处理器,而在责任链中单个请求对象可以有许多处理器。

设计参与者

命令设计模式的参与者包括:

  • 命令接口 – 用于声明操作。
  • 具体命令类 – 扩展了Command接口,并具有用于在接收方上调用业务操作方法的执行方法。 它在内部具有命令接收者的参考。
  • 调用者 – 被赋予执行操作的命令对象。
  • 接收器 – 执行操作。

在命令模式中,调用者与接收者执行的动作分离。 调用者不知道接收者。 调用者调用一个命令,然后该命令执行接收方的相应操作。 因此,调用者可以在不知道要执行的动作的细节的情况下调用命令。 此外,这种脱钩意味着对接收者动作的更改不会直接影响动作的调用。

问题陈述

假设我们需要为家庭自动化系统构建一个遥控器,该遥控器应控制房屋的不同照明/电气单元。 遥控器中的单个按钮可能能够在类似设备上执行相同的操作,例如,电视开/关按钮可用于打开/关闭不同房间中的不同电视机。

在这里,此遥控器将是一个可编程的遥控器,它将用于打开和关闭各种灯/风扇等。

首先,让我们看看如何使用任何设计方法解决问题。 她的遥控器代码可能看起来像:

  1. If(buttonName.equals(“Light”))
  2. {
  3. //Logic to turn on that light
  4. }
  5. else If(buttonName.equals(“Fan”))
  6. {
  7. //Logic to turn on that Fan
  8. }

但是上述解决方案显然存在许多明显的问题,例如:

  • 任何新项目(例如TubeLight)都需要更改遥控器的代码。 您将需要添加更多的if-else
  • 如果我们想为其他目的更改按钮,那么我们也需要更改代码。
  • 最重要的是,如果家里有很多项目,代码的复杂性和可维护性将会增加。
  • 最后,代码不干净且紧密耦合,我们未遵循为接口编码等最佳实践。

命令模式实现

让我们用命令设计模式解决上述家庭自动化问题,并一次设计一个组件。

  • ICommand接口是命令接口
  • Light接收器组件之一。 它可以接受与Light有关的多个命令,例如打开和关闭
  • Fan也是接收器组件的另一种类型。 它可以接受与风扇相关的多个命令,例如打开和关闭
  • HomeAutomationRemote调用者对象,它要求命令执行请求。 这里风扇开/关,灯开/关。
  • StartFanCommandStopFanCommandTurnOffLightCommandTurnOnLightCommand等是命令实现的不同类型。

类图

命令设计模式 - 图1

命令模式类图

让我们看一下每个类和接口的 java 源代码。

ICommand.java

  1. package com.howtodoinjava.designpattern.command.homeautomation;
  2. /**
  3. * Command Interface which will be implemented by the exact commands.
  4. *
  5. */
  6. @FunctionalInterface
  7. public interface ICommand {
  8. public void execute();
  9. }

Light.java

  1. package com.howtodoinjava.designpattern.command.homeautomation.light;
  2. /**
  3. * Light is a Receiver component in command pattern terminology.
  4. *
  5. */
  6. public class Light {
  7. public void turnOn() {
  8. System.out.println("Light is on");
  9. }
  10. public void turnOff() {
  11. System.out.println("Light is off");
  12. }
  13. }

Fan.java

  1. package com.howtodoinjava.designpattern.command.homeautomation.fan;
  2. /**
  3. * Fan class is a Receiver component in command pattern terminology.
  4. *
  5. */
  6. public class Fan {
  7. void start() {
  8. System.out.println("Fan Started..");
  9. }
  10. void stop() {
  11. System.out.println("Fan stopped..");
  12. }
  13. }

TurnOffLightCommand.java

  1. package com.howtodoinjava.designpattern.command.homeautomation.light;
  2. import com.howtodoinjava.designpattern.command.homeautomation.ICommand;
  3. /**
  4. * Light Start Command where we are encapsulating both Object[light] and the
  5. * operation[turnOn] together as command. This is the essence of the command.
  6. *
  7. */
  8. public class TurnOffLightCommand implements ICommand {
  9. Light light;
  10. public TurnOffLightCommand(Light light) {
  11. super();
  12. this.light = light;
  13. }
  14. public void execute() {
  15. System.out.println("Turning off light.");
  16. light.turnOff();
  17. }
  18. }

TurnOnLightCommand.java

  1. package com.howtodoinjava.designpattern.command.homeautomation.light;
  2. import com.howtodoinjava.designpattern.command.homeautomation.ICommand;
  3. /**
  4. * Light stop Command where we are encapsulating both Object[light] and the
  5. * operation[turnOff] together as command. This is the essence of the command.
  6. *
  7. */
  8. public class TurnOnLightCommand implements ICommand {
  9. Light light;
  10. public TurnOnLightCommand(Light light) {
  11. super();
  12. this.light = light;
  13. }
  14. public void execute() {
  15. System.out.println("Turning on light.");
  16. light.turnOn();
  17. }
  18. }

StartFanCommand.java

  1. package com.howtodoinjava.designpattern.command.homeautomation.fan;
  2. import com.howtodoinjava.designpattern.command.homeautomation.ICommand;
  3. /**
  4. * Fan Start Command where we are encapsulating both Object[fan] and the
  5. * operation[start] together as command. This is the essence of the command.
  6. *
  7. */
  8. public class StartFanCommand implements ICommand {
  9. Fan fan;
  10. public StartFanCommand(Fan fan) {
  11. super();
  12. this.fan = fan;
  13. }
  14. public void execute() {
  15. System.out.println("starting Fan.");
  16. fan.start();
  17. }
  18. }

StopFanCommand.java

  1. package com.howtodoinjava.designpattern.command.homeautomation.fan;
  2. import com.howtodoinjava.designpattern.command.homeautomation.ICommand;
  3. /**
  4. * Fan stop Command where we are encapsulating both Object[fan] and the
  5. * operation[stop] together as command. This is the essence of the command.
  6. *
  7. */
  8. public class StopFanCommand implements ICommand {
  9. Fan fan;
  10. public StopFanCommand(Fan fan) {
  11. super();
  12. this.fan = fan;
  13. }
  14. public void execute() {
  15. System.out.println("stopping Fan.");
  16. fan.stop();
  17. }
  18. }

HomeAutomationRemote.java

  1. package com.howtodoinjava.designpattern.command.homeautomation;
  2. /**
  3. * Remote Control for Home automation where it will accept the command and
  4. * execute. This is the invoker in terms of command pattern terminology
  5. */
  6. public class HomeAutomationRemote {
  7. //Command Holder
  8. ICommand command;
  9. //Set the command in runtime to trigger.
  10. public void setCommand(ICommand command) {
  11. this.command = command;
  12. }
  13. //Will call the command interface method so that particular command can be invoked.
  14. public void buttonPressed() {
  15. command.execute();
  16. }
  17. }

演示

让我们编写代码并执行客户端代码,以查看命令的执行方式。

  1. package com.howtodoinjava.designpattern.command.homeautomation;
  2. import com.howtodoinjava.designpattern.command.homeautomation.fan.Fan;
  3. import com.howtodoinjava.designpattern.command.homeautomation.fan.StartFanCommand;
  4. import com.howtodoinjava.designpattern.command.homeautomation.fan.StopFanCommand;
  5. import com.howtodoinjava.designpattern.command.homeautomation.light.Light;
  6. import com.howtodoinjava.designpattern.command.homeautomation.light.TurnOnLightCommand;
  7. /**
  8. * Demo class for HomeAutomation
  9. *
  10. */
  11. public class Demo //client
  12. {
  13. public static void main(String[] args)
  14. {
  15. Light livingRoomLight = new Light(); //receiver 1
  16. Fan livingRoomFan = new Fan(); //receiver 2
  17. Light bedRoomLight = new Light(); //receiver 3
  18. Fan bedRoomFan = new Fan(); //receiver 4
  19. HomeAutomationRemote remote = new HomeAutomationRemote(); //Invoker
  20. remote.setCommand(new TurnOnLightCommand( livingRoomLight ));
  21. remote.buttonPressed();
  22. remote.setCommand(new TurnOnLightCommand( bedRoomLight ));
  23. remote.buttonPressed();
  24. remote.setCommand(new StartFanCommand( livingRoomFan ));
  25. remote.buttonPressed();
  26. remote.setCommand(new StopFanCommand( livingRoomFan ));
  27. remote.buttonPressed();
  28. remote.setCommand(new StartFanCommand( bedRoomFan ));
  29. remote.buttonPressed();
  30. remote.setCommand(new StopFanCommand( bedRoomFan ));
  31. remote.buttonPressed();
  32. }
  33. }

输出:

  1. Turning on light.
  2. Light is on
  3. Turning on light.
  4. Light is on
  5. starting Fan.
  6. Fan Started..
  7. stopping Fan.
  8. Fan stopped..
  9. starting Fan.
  10. Fan Started..
  11. stopping Fan.
  12. Fan stopped..

何时使用命令模式

您可以使用命令模式来解决许多设计问题,例如:

  • 处理 Java 菜单项和按钮的动作。
  • 提供对宏的支持(宏的记录和播放)。
  • 提供“撤消”支持。
  • 进度条实现。
  • 创建多步骤向导。

流行的命令模式实现

这些是命令模式实现的一些实际示例:

  • 可运行接口(java.lang.Runnable
  • Swing Actionjavax.swing.Action)使用命令模式
  • ActionServlet调用 Struts Action类在内部使用命令模式。

总结

  1. 命令模式是一种行为型设计模式。
  2. 在命令模式中,对象用于封装在任何时间执行操作或触发事件所需的所有信息,从而使开发人员能够分散业务逻辑。 它称为命令。
  3. 客户端与调用者对话并传递命令对象。
  4. 每个命令对象都有对其接收者的引用。
  5. Commandexecute()方法调用接收器中定义的实际业务操作。
  6. 接收者执行业务操作。

这是此模式的一些利弊。 它可以帮助您就使用命令模式做出正确的决定。

优点

  • 使我们的代码具有可伸缩性,因为我们可以添加新命令而无需更改现有代码。
  • 使用命令对象增加调用者和接收者之间的松散耦合。

缺点

  • 在不同的视图中,增加每个单独命令的类数。 在某些特定情况下,它可能不是首选。

这就是命令设计模式的全部。 将我的问题放在评论部分。

下载源码

学习愉快!