【设计模式】命令模式(Command Pattren)

命令模式(Command Pattren)属于行为型模式,它又被称做动作(Action)模式事务(Transaction)模式。命令模式将请求者发送的请求封装为命令对象,这个命令对象将携带请求者的相关信息被传递到接收者,以此来达到请求者和接收者的解耦操作,让对象之间的调用更加灵活。在软件系统中,行为请求者行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适,所以需要使用命令模式。


命令模式的简介

命令模式(Command Pattren)的本质是对命令进行封装,它将发出命令的责任和执行命令的责任分割开,委派给不同的对象,此时发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。命令模式的核心在于引入了命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再通过命令对象来调用请求接收者的处理方法命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能


优点

  • 请求者和接收者解耦,降低了系统耦合度
  • 易于拓展新的命令,新的命令很容易地被加入到系统里
  • 可以容易地实现对请求的撤销和恢复
  • 可以比较容易地设计一个组合命令
  • 在需要的情况下,可以较容易地将命令记入日志

缺点

  • 每一个命令都需要设计一个具体命令类,后期命令类过多可能影响性能


应用场景

  • 需要用到“撤销/恢复、记录日志、事务”的场景
  • GUI 中每一个按钮都是一条命令
  • 模拟 CMD中的命令



命令模式的应用

假设现在我要开发一个简单的文本框,文本框具有显示文本和撤销文本的功能。此时我需要先设计两个按钮类,分别为显示按钮**(ShowButton)和撤销按钮(RevokeButton),这两个类主要是做UI渲染方面的事情,而业务逻辑通过命令类(ShowCommand和RevokeCommand)传递到文本框类(TextBox)来执行。同时向文本框输入文本也写了一个输入器类(InputHandle)来模拟,输入器发送键入命令(InputCommand)**到文本框,文本框存储这些命令,为之后的撤销操作做准备。


类图

image-20230204201754730


五种角色:

  • **客户端(Client)角色:**创建一个具体命令(ConcreteCommand)对象并确定其接收者。
  • **命令(Command)角色:**声明了一个所有具体命令类的抽象接口。
  • **具体命令(ConcreteCommand)角色:**定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
  • 请求者(Invoker)角色:负责调用命令对象执行请求,如execute()方法。
  • **接收者(Receiver)角色:**负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。



命令模式的实现

第一步:编写命令角色

Command
package 设计模式.行为型模式.命令模式;

public interface Command {

    boolean execute();

}

第二步:编写接收者角色

TextBox
package 设计模式.行为型模式.命令模式;

import java.util.ArrayList;

public class TextBox {

    private String content = ""; // 文本框中的文本

    ArrayList<InputCommand> commandList = new ArrayList<>(); // 等待撤销的键入命令列表

    /**
     * 输入方法,获得待执行命令中的信息,将新的信息添加到当前文本中
     * @param command 待执行的键入命令
     */
    public void input(InputCommand command){
        content += command.getText(); // 获取命令中的文本,添加到当前文本
        command.clear(); // 清除命令中的文本,节省资源
        commandList.add(command); // 将当前命令添加到键入命令列表,方便撤销操作
    }

    /**
     * 撤销方法,为文本框提供撤销操作
     */
    public void revoke(){
        if (commandList.size() < 1) return; // 如果命令列表中没有命令则不执行
        int index = commandList.size() - 1; // 获取最后一条命令的索引
        InputCommand command = commandList.get(index); // 获取最后一条命令
        content = content.substring(0,content.length()-command.getTextSize()); // 进行撤销操作
        commandList.remove(index); // 删除最后一条键入命令
    }

    public void print(){
        // 调用显示器显示文本
        System.out.println(content);
    }

}

第三步:编写具体命令角色

InputCommand
package 设计模式.行为型模式.命令模式;

public class InputCommand implements Command {

    private String text; // 待输入的文本
    private TextBox textBox; //

    private int textSize = -1;

    InputCommand(TextBox textBox){
        this.textBox = textBox;
    }

    public void setText(String text) {
        this.text = text;
        textSize = text.length(); // 获得文本长度
    }

    public String getText(){
        return text;
    }

    public int getTextSize() {
        return textSize;
    }

    /**
     * 清除当前对象存储的键入文本信息,节省资源,由文本框调用
     */
    public void clear(){
        text = null;
    }


    @Override
    public boolean execute() {
        // 执行命令,如果文本存在则执行成功,否则执行失败
        if (text != null) textBox.input(this);
        else return false;
        return true;
    }
}
ShowCommand
package 设计模式.行为型模式.命令模式;

public class ShowCommand implements Command {

    TextBox textBox; // 在显示命令中维护接收者(文本框)

    ShowCommand(TextBox textBox){
        this.textBox = textBox;
    }

    @Override
    public boolean execute() {
        // 执行文本框的显示方法
        if (textBox != null) textBox.print();
        else return false;
        return true;
    }
}
RevokeCommand
package 设计模式.行为型模式.命令模式;

public class RevokeCommand implements Command {

    private TextBox textBox; // 在撤销命令中维护接收者(文本框)

    public RevokeCommand(TextBox textBox) {
        this.textBox = textBox;
    }

    @Override
    public boolean execute() {
        // 执行调用者的撤销方法
        if (textBox != null) textBox.revoke();
        else return false;
        return true;
    }
}

第四步:编写请求者角色

InputHandle
package 设计模式.行为型模式.命令模式;

/**
 * 模拟一个输入器
 */
public class InputHandle {

    private TextBox textBox; // 指定一个文本框,用于键入文本
    private String text = ""; // 模拟缓存区文本

    public InputHandle(TextBox textBox){
        this.textBox = textBox; // 维护一个文本框
    }

    public void setText(String text){ // 设置输入器的缓存文字
        this.text += text;
    }

    /**
     * 键入方法,将缓存区的文本通过命令写入到指定文本框中
     */
    public void typing(){
        // 将键入命令实体化,方便文本撤销操作
        // 指定键入命令输出的文本框
        InputCommand command = new InputCommand(textBox);
        // 指定键入命令输入入的文字
        command.setText(text);
        // 如果该命令执行成功,清除缓存
        if (command.execute()) text = "";
    }
}
ShowButton
package 设计模式.行为型模式.命令模式;

public class ShowButton {

    private Command command; // 维护一条显示命令

    ShowButton(Command command){
        this.command = command;
    }

    public void onClick(){
        if (command != null) command.execute();
    }
}
RevokeButton
package 设计模式.行为型模式.命令模式;

/**
 * 撤销按钮
 */
public class RevokeButton {

    private TextBox textBox; // 维护一个文本框

    public RevokeButton(TextBox textBox){
        this.textBox = textBox;
    }

    public void onClick(){
        // 点击撤销按钮时,新建一条撤销命令,并执行
        new RevokeCommand(textBox).execute();
    }
}

第五步:编写客户程序

CommandMain
package 设计模式.行为型模式.命令模式;

public class CommandMain {
    public static void main(String[] args) {
        // 新建一个文本框
        TextBox textBox = new TextBox();
        // 新建一个输入器,用来模拟键盘输入
        InputHandle inputHandle = new InputHandle(textBox);
        // 新建一个显示按钮,在按钮中维护显示指令,在显示指令中维护一个文本框,以此达到点击按钮显示文本的操作
        ShowButton showButton = new ShowButton(new ShowCommand(textBox));
        // 新建一个撤销按钮,在撤销按钮中维护一个文本框
        RevokeButton revokeButton = new RevokeButton(textBox);

        //缓存文本“你好世界!”
        inputHandle.setText("你好世界!");
        // 通过键入命令键入到文本框
        inputHandle.typing();
        // 点击按钮,让文本框的内容显示在屏幕上
        showButton.onClick();
        // 再次键入文本
        inputHandle.setText("hello,world!");
        inputHandle.typing();
        showButton.onClick();
        //再次键入文本
        inputHandle.setText("abc!");
        inputHandle.typing();
        showButton.onClick();

        // 点击撤销按钮,在内部执行撤销命令
        revokeButton.onClick();
        // 显示撤销后的文本框文本信息
        showButton.onClick();
        revokeButton.onClick();
        showButton.onClick();
    }
}

第六步:测试

image-20230204202952231

你好世界!
你好世界!hello,world!
你好世界!hello,world!abc!
你好世界!hello,world!
你好世界!

Process finished with exit code 0
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值