命令模式(Command Pattern)

2024/3/29 Java进阶

将请求封装成一个对象,以便使用命令来参数化其它对象,或者将命令对象放入队列中进行排队,或者将命令对象的操作记录到日志中,以及支持可撤销的操作。

# 1、命令模式案例

一个文本对象,将各个方法请求封装成对象,将一个个命令对象放入队列中,通过执行者以命令的方式进行调用

public class TextContent {

    private String content = "";

    // 新增请求
    public void addStr(String content){
        this.content = content;
    }

    // 删除请求
    public void delLastStr(){
        if(!content.isEmpty()){
            content = content.substring(0,content.length()-1);
        }
    }

    public String getContent(){
        return content;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

将方法请求封装成命令对象

public interface TextCommand {
    void execute();
}

// 新增请求封装为命令对象
public class AddStrCommand implements TextCommand {

    private TextContent textContent;

    private String content;

    public AddStrCommand(TextContent textContent,String content) {
        this.textContent = textContent;
        this.content = content;
    }

    @Override
    public void execute() {
        textContent.addStr(textContent.getContent()+content);
    }
}

// 删除最后一个字符封装为命令对象
public class RemoveLastStrCommand implements TextCommand {
    private TextContent textContent;

    public RemoveLastStrCommand(TextContent textContent) {
        this.textContent = textContent;
    }

    @Override
    public void execute() {
        textContent.delLastStr();
    }
}
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

命令调用者,这里直接用队列封装

public class TextCommandInvoker {

    private final Queue<TextCommand> commandQueue = new LinkedList<>();

    public void addCommand(TextCommand command){
        commandQueue.offer(command);
    }

    public void executionCommand(){
        while (!commandQueue.isEmpty()){
            commandQueue.poll().execute();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ClientTest {
    public static void main(String[] args) {
        // 影响结果的对象
        TextContent textContent = new TextContent();
        // 执行者
        TextCommandInvoker invoker = new TextCommandInvoker();

        // 将命令给执行者
        invoker.addCommand(new AddStrCommand(textContent, "AAA;"));   // AAA; 但没执行命令所以为空
        invoker.addCommand(new AddStrCommand(textContent, "AAA;"));   // AAA;AAA; 但没执行命令所以为空
        System.out.println("textContent1: "+textContent.getContent());

        // 将命令给执行者
        invoker.addCommand(new RemoveLastStrCommand(textContent));  // AAA;AAA 但没执行命令所以为空
        System.out.println("textContent2: "+textContent.getContent());
        invoker.addCommand(new RemoveLastStrCommand(textContent));  // AAA;AA 但没执行命令所以为空

        // 执行者统一执行命令
        invoker.executionCommand();  // 执行命令:AAA;AA
        System.out.println("textContent3: "+textContent.getContent());
    }
}
/**
 * 输出结果:
 * textContent1: 
 * textContent2: 
 * textContent3: AAA;AA
 * /
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

# 2、增加撤回功能

TextContent不用改,我们只需要在相应命令对象方法增加自己功能的撤回就行

public interface TextCommand {
    void execute();

    void undo();
}
1
2
3
4
5
public class AddStrCommand implements TextCommand {

    private TextContent textContent;

    // 记录增加的内容
    private String content;

    public AddStrCommand(TextContent textContent,String content) {
        this.textContent = textContent;
        this.content = content;
    }

    @Override
    public void execute() {
        textContent.addStr(textContent.getContent()+content);
    }

    @Override
    public void undo() {
        textContent.addStr(textContent.getContent().replaceFirst(content,""));
    }
}

public class RemoveLastStrCommand implements TextCommand {
    private TextContent textContent;

    // 记录删除的内容
    private String delStr;

    public RemoveLastStrCommand(TextContent textContent) {
        this.textContent = textContent;
    }

    @Override
    public void execute() {
        this.delStr = textContent.getContent().substring(textContent.getContent().length()-1);
        textContent.delLastStr();
    }

    @Override
    public void undo() {
        textContent.addStr(textContent.getContent()+delStr);
    }
}
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
39
40
41
42
43
44
public class TextCommandInvoker {

    private final Queue<TextCommand> commandQueue = new LinkedList<>();

    // 记录历史
    private final Deque<TextCommand> historyDeque = new ArrayDeque<>();

    public void addCommand(TextCommand command){
        commandQueue.offer(command);
    }

    public void executionCommand(){
        while (!commandQueue.isEmpty()){
            TextCommand command = commandQueue.poll();
            historyDeque.addLast(command);
            command.execute();
        }
    }

    public void undoCommand(){
        if(!historyDeque.isEmpty()){
            historyDeque.pollLast().undo();
        }else {
            System.out.println("没有可撤销操作");
        }
    }

}
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
public class ClientTest {
    public static void main(String[] args) {
        // 最终成果
        TextContent textContent = new TextContent();
        // 执行者
        TextCommandInvoker invoker = new TextCommandInvoker();

        // 将命令给执行者
        invoker.addCommand(new AddStrCommand(textContent, "AAA;"));   // AAA; 但没执行命令所以为空
        invoker.addCommand(new AddStrCommand(textContent, "AAA;"));   // AAA;AAA; 但没执行命令所以为空
        System.out.println("textContent1: "+textContent.getContent());

        // 将命令给执行者
        invoker.addCommand(new RemoveLastStrCommand(textContent));  // AAA;AAA 但没执行命令所以为空
        System.out.println("textContent2: "+textContent.getContent());
        invoker.addCommand(new RemoveLastStrCommand(textContent));  // AAA;AA 但没执行命令所以为空

        // 执行者统一执行命令
        invoker.executionCommand();  // 执行命令:AAA;AA
        System.out.println("textContent3: "+textContent.getContent());

        // 撤销命令
        invoker.undoCommand();  // 撤销一次:AAA;AA  ===>  AAA;AAA
        System.out.println("textContent4: "+textContent.getContent());
        invoker.undoCommand();  // 撤销第二次:AAA;AAA ===> AAA;AAA;
        System.out.println("textContent5: "+textContent.getContent());
        invoker.undoCommand();  // 撤销第三次:AAA;AAA; ===> AAA;
        System.out.println("textContent6: "+textContent.getContent());
        invoker.undoCommand();
        invoker.undoCommand();
    }
}
/**
 * 输出结果:
 * textContent1: 
 * textContent2: 
 * textContent3: AAA;AA
 * textContent4: AAA;AAA
 * textContent5: AAA;AAA;
 * textContent6: AAA;
 * 没有可撤销操作
 * /
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
39
40
41
42