104.md 11.0 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# 命令设计模式

> 原文: [https://howtodoinjava.com/design-patterns/behavioral/command-pattern/](https://howtodoinjava.com/design-patterns/behavioral/command-pattern/)

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

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

```java
Table of Contents
Introduction
Design Participants
Problem Statement
Command Pattern Implementation
Demo
When to Use Command Pattern
Popular Implementations
Summary

```

## 介绍

在面向对象的编程中,命令模式是一种行为设计模式,其中一个对象用于[封装](//howtodoinjava.com/object-oriented/encapsulation-in-java-and-its-relation-with-abstraction/)执行动作,业务操作或触发事件所需的所有信息。 方法名称,接收方对象引用和方法参数值(如果有)。 该对象称为*命令*

类似的方法也适用于[责任链](//howtodoinjava.com/design-patterns/behavioral/chain-of-responsibility-design-pattern/)模式。 唯一的区别是命令中只有一个请求处理程序,而在责任链中单个请求对象可以有许多处理程序。

## 设计参加者

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

W
wizardforcel 已提交
32 33 34 35
*   **命令界面** – 用于声明操作。
*   **具体命令类** – 扩展了`Command`接口,并具有用于在接收方上调用业务操作方法的执行方法。 它在内部具有命令接收者的参考。
*   **调用程序** – 被赋予执行操作的命令对象。
*   **接收器** – 执行操作。
W
wizardforcel 已提交
36 37 38 39 40 41 42 43 44

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

## 问题陈述

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

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

W
wizardforcel 已提交
45
首先,让我们看看如何使用任何设计方法解决问题。 她的遥控器代码可能看起来像:
W
wizardforcel 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58

```java
If(buttonName.equals(Light))
{
	//Logic to turn on that light
}
else If(buttonName.equals(Fan))
{
	//Logic to turn on that Fan
}

```

W
wizardforcel 已提交
59
但是上述解决方案显然存在许多明显的问题,例如:
W
wizardforcel 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

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

## 命令模式实施

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

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

#### 类图

W
wizardforcel 已提交
78
![Class Diagram](img/9ec819f9f1332ed80df50bd0356b0724.png)
W
wizardforcel 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

Command Pattern Class Diagram



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

`ICommand.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation;

/**
 * Command Interface which will be implemented by the exact commands.
 *
 */
@FunctionalInterface
public interface ICommand {
	public void execute();
}

```

`Light.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation.light;

/**
 * Light is a Receiver component in command pattern terminology.
 *
 */
public class Light {
	public  void turnOn() {
		System.out.println("Light is on");
	}

	public void turnOff() {
		System.out.println("Light is off");
	}
}

```

`Fan.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation.fan;

/**
 * Fan class is a Receiver component in command pattern terminology.
 *
 */
public class Fan {
	void start() {
		System.out.println("Fan Started..");

	}

	 void stop() {
		System.out.println("Fan stopped..");

	}
}

```

`TurnOffLightCommand.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation.light;

import com.howtodoinjava.designpattern.command.homeautomation.ICommand;
/**
 * Light Start Command where we are encapsulating both Object[light] and the
 * operation[turnOn] together as command. This is the essence of the command.
 *
 */
public class TurnOffLightCommand implements ICommand {

	Light light;

	public TurnOffLightCommand(Light light) {
		super();
		this.light = light;
	}

	public void execute() {
		System.out.println("Turning off light.");
		light.turnOff();
	}
}

```

`TurnOnLightCommand.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation.light;

import com.howtodoinjava.designpattern.command.homeautomation.ICommand;

/**
 * Light stop Command where we are encapsulating both Object[light] and the
 * operation[turnOff] together as command. This is the essence of the command.
 *
 */
public class TurnOnLightCommand implements ICommand {

	Light light;

	public TurnOnLightCommand(Light light) {
		super();
		this.light = light;
	}

	public void execute() {
		System.out.println("Turning on light.");
		light.turnOn();
	}
}

```

`StartFanCommand.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation.fan;

import com.howtodoinjava.designpattern.command.homeautomation.ICommand;

/**
 * Fan Start Command where we are encapsulating both Object[fan] and the
 * operation[start] together as command. This is the essence of the command.
 *
 */
public class StartFanCommand implements ICommand {

	Fan fan;

	public StartFanCommand(Fan fan) {
		super();
		this.fan = fan;
	}

	public void execute() {
		System.out.println("starting Fan.");
		fan.start();
	}
}

```

`StopFanCommand.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation.fan;

import com.howtodoinjava.designpattern.command.homeautomation.ICommand;
/**
 * Fan stop Command where we are encapsulating both Object[fan] and the
 * operation[stop] together as command. This is the essence of the command.
 *
 */
public class StopFanCommand implements ICommand {

	Fan fan;

	public StopFanCommand(Fan fan) {
		super();
		this.fan = fan;
	}

	public void execute() {
		System.out.println("stopping Fan.");
		fan.stop();
	}
}

```

`HomeAutomationRemote.java`

```java
package com.howtodoinjava.designpattern.command.homeautomation;

/**
 * Remote Control for Home automation where it will accept the command and
 * execute. This is the invoker in terms of command pattern terminology
 */
public class HomeAutomationRemote {

	//Command Holder
	ICommand command;

	//Set the command in runtime to trigger.
	public void setCommand(ICommand command) {
		this.command = command;
	}

	//Will call the command interface method so that particular command can be invoked.
	public void buttonPressed() {
		command.execute();
	}
}

```

## 演示版

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

```java
package com.howtodoinjava.designpattern.command.homeautomation;

import com.howtodoinjava.designpattern.command.homeautomation.fan.Fan;
import com.howtodoinjava.designpattern.command.homeautomation.fan.StartFanCommand;
import com.howtodoinjava.designpattern.command.homeautomation.fan.StopFanCommand;
import com.howtodoinjava.designpattern.command.homeautomation.light.Light;
import com.howtodoinjava.designpattern.command.homeautomation.light.TurnOnLightCommand;

/**
 * Demo class for HomeAutomation
 *
 */
public class Demo 	//client
{
	public static void main(String[] args) 
	{
		Light livingRoomLight = new Light();	//receiver 1

		Fan livingRoomFan = new Fan();	//receiver 2

		Light bedRoomLight = new Light();	//receiver 3

		Fan bedRoomFan = new Fan();		//receiver 4

		HomeAutomationRemote remote = new HomeAutomationRemote();	//Invoker

		remote.setCommand(new TurnOnLightCommand( livingRoomLight ));
		remote.buttonPressed();

		remote.setCommand(new TurnOnLightCommand( bedRoomLight ));
		remote.buttonPressed();

		remote.setCommand(new StartFanCommand( livingRoomFan ));
		remote.buttonPressed();

		remote.setCommand(new StopFanCommand( livingRoomFan ));
		remote.buttonPressed();

		remote.setCommand(new StartFanCommand( bedRoomFan ));
		remote.buttonPressed();

		remote.setCommand(new StopFanCommand( bedRoomFan ));
		remote.buttonPressed();
	}
}

```

输出:

```java
Turning on light.
Light is on

Turning on light.
Light is on

starting Fan.
Fan Started..

stopping Fan.
Fan stopped..

starting Fan.
Fan Started..

stopping Fan.
Fan stopped..

```

## 何时使用命令模式

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

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

## 流行的命令模式实现

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

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

## 摘要

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

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

#### 优点

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

#### 缺点

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

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

[Download Source Code](//howtodoinjava.com/wp-content/downloads/Command-Design-Pattern.zip)

学习愉快!