设计模式6-命令模式与组合模式

命令模式

定义

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

主要解决

在软件系统中,行为请求者行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

优缺点

优点

  1. 通过引入中间件(抽象接口)降低系统的耦合度。
  2. 扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
  3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。
  5. 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

缺点

  1. 可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
  2. 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。

实现

主要角色

  1. 抽象命令类(abstract command):声明执行的接口,拥有执行命令的抽象方法execute()。
  2. 具体命令类(concrete command):是抽象类的具体实现类,它拥有接收者对象,并通过调用接受者的功能来完成命令要执行的操作。
  3. 接收者(receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  4. 调用者(invoker):是请求的发送者,他通常拥有很多的命令对象,并通过访问命令对象来执行相关操作,它不直接访问接收者。

代码

这里以一个

  1. 抽象命令接口
1
2
3
4
//Order.java
public interface Order {
void execute();
}
  1. 具体命令类
1
2
3
4
5
6
7
8
9
10
11
12
//BuyStock.java
public class BuyStock implements Order {
private Stock abcStock;

public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}

public void execute() {
abcStock.buy();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
//BuyStock.java
public class BuyStock implements Order {
private Stock abcStock;

public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}

public void execute() {
abcStock.buy();
}
}
  1. 请求类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Stock.java

public class Stock {

private String name = "ABC";
private int quantity = 10;

public void buy(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] bought");
}
public void sell(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] sold");
}
}
  1. 命令调用类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Broker.java

import java.util.ArrayList;
import java.util.List;

public class Broker {
private List<Order> orderList = new ArrayList<Order>();

public void takeOrder(Order order){
orderList.add(order);
}

public void placeOrders(){
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
  1. 使用 Broker 类来接受并执行命令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CommandPatternDemo {
public static void main(String[] args) {
Stock abcStock = new Stock();

//新建具体命令
BuyStock buyStockOrder = new BuyStock(abcStock);
SellStock sellStockOrder = new SellStock(abcStock);

//创建执行类
Broker broker = new Broker();
broker.takeOrder(buyStockOrder);
broker.takeOrder(sellStockOrder);

//执行
broker.placeOrders();
}
}

图解

命令模式

JavaScript中的命令模式

在JavaScript中,我们无须那么麻烦的创建类。由于函数在JS中作为一等公民,本身就可以作为参数进行传递。所以不一定要将其封装在execute方法中,而是直接将其作为字面量对象的成员进行传递。

比如按钮点击事件的添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let bindClick = function(button, func){
button.onClick = func
}

let MenuBar = {
refresh: function(){
console.log('刷新子菜单')
}
}

let SubMenu = {
add: function(){
console.log('添加子菜单')
},
del: function(){
consle.log('删除子菜单')
}
}

bindClick(button1, MenuBar.refresh)
bindClick(button2, SubMenu.add)
bindClick(button3, SubMenu.del)

组合模式

定义

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

主要解决

它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

宏命令

宏命令包含了一组具体的子命令对象,不管是宏命令,还是子命令,都有一个execute方法负责执行命令。

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
32
33
34
35
36
37
38
let CloseDoorCommand = {
execute: function(){
console.log('关门')
}
}

let openPCCommand = {
execute: function(){
consoloe.log('开电脑')
}
}

let openQQCommand = {
execute: function(){
console.log('登录QQ')
}
}

let MacroCommand = function(){
return {
commandList : [],
add: function(command){
this.commandList.push(command)
},
execute: function(){
for(let c of this,commandList){
c.execute()
}
}
}
}

let macroCommand = MacroCommand()
macroCommand.add(CloseDoorCommand)
macroCommand.add(openPCCommand)
macroCommand.add(openQQCommand)

macroCommand.execute()

在组合模式中,请求在树中传递总是遵循一种逻辑。

请求从树最顶端的对象向下传递,如果当前请求的对象是叶对象(普通子命令),也对象自身会对请求做出相应的处理;如果当前请求的对象是组合对象(宏命令),组合对象则会遍历它下属的子节点,将请求传递给这些子节点。

图解

组合模式

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :