当前位置: 首页 > news >正文

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

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

文章目录

  • 命令模式的简介
    • 优点
    • 缺点
    • 应用场景
  • 命令模式的应用
    • 类图
      • 五种角色:
    • 命令模式的实现
      • 第一步:编写命令角色
        • Command
      • 第二步:编写接收者角色
        • TextBox
      • 第三步:编写具体命令角色
        • InputCommand
        • ShowCommand
        • RevokeCommand
      • 第四步:编写请求者角色
        • InputHandle
        • ShowButton
        • RevokeButton
      • 第五步:编写客户程序
        • CommandMain
      • 第六步:测试


命令模式的简介

命令模式(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

相关文章:

  • Flutter局部刷新解决闪烁问题
  • hive的数据库和表操作
  • 力扣第331场周赛题解
  • 【Java】Spring Cloud 教程
  • 44 计算概论与程序设计基础21h-北京大学
  • Android-Native开发系列之利用AAudio播放音频
  • 道路病害识别监测系统 CNN网络
  • 基于wxbot的微信聊天机器人
  • 【OLAP】ClickHouse学习笔记
  • Android 插件化中资源错乱的解决方案
  • 【游戏逆向】FPS网络游戏自动瞄准漏洞分析以及实现
  • crawlspider类的使用
  • unity实时渲染部分
  • 【Linux】冯诺依曼体系与操作系统(OS)概念
  • Trie树的构造与应用
  • softmaxsigmoid
  • 【每日一题Day109】LC1210穿过迷宫的最少移动次数 | BFS+dp
  • 基于蜣螂算法优化的广义神经网络(GRNN)预测-附代码
  • 基于linux5.15.5的IMX 参考手册 ---20
  • ((蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第5天】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有62天
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉