设计模式之命令模式

1. 定义

在软件系统中,行为请求者行为实现者通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)。[引自百度百科]

定义概括:
1. 行为请求者与行为实现者解耦
2. 行为抽象为对象

2.类图


这里写图片描述

 Command模式中几种角色:

  • Command(命令): 负责定义命令的接口

  • ConcreteCommand(具体的命令):实现 Command接口的类,其中也持有了Receiver角色的引用。

  • Receiver(接收者): 是Command角色执行命令时的对象,是真正执行命令的对象,在Command命令的execute()方法里被调用。

  • Invoker(发动者):开始执行命令的角色,它会调用Command角色中定义的接口。

  • Client(请求者): 负责生成ConcreteCommand实例并将Receiver角色分配给ConcreteCommand(设置命令对象的接收者)。它不是常规意义的客户端,而是组装了ConcreteCommand 角色和Receiver角色。

3. 例子

 举个例子说明一下吧。

 编写一个简单的画图软件,用户拖动鼠标时绘制红点,点击clear按钮清除所有绘制的轨迹。

 首先定义抽象的命令接口:

public interface Command {

    void execute();
}

 定义Drawable接口:

public interface Drawable {

    void draw(int x, int y);
}

 定义一个绘制一个点的具体命令DrawCommand:

import java.util.Iterator;
import java.util.Stack;
public class DrawCommand implements Command {

    protected Drawable drawable; //Receiver角色,真正执行命令的角色

    private Point position;

    //该构造方法被Client角色调用,将具体的Receiver角色传入进来
    public DrawCommand(Drawable drawable, Point position) {
        this.drawable = drawable;
        this.position = position;
    }

    @Override
    public void execute() {
        //Receiver角色执行
        drawable.draw(position.x, position.y);
    }
}

 定义MacroCommand类管理所有的命令集合:

public class MacroCommand implements Command {

    //命令的集合
    private Stack<Command> commands = new Stack<>();

    @Override
    public void execute() {
        Iterator<Command> iter = commands.iterator();
        while (iter.hasNext()) {
            iter.next().execute();
        }
    }

    //添加一条命令
    public void append(Command command) {
        if (command != this) {
            commands.push(command);
        }
    }

    //清除所有命令
    public void clear() {
        commands.clear();
    }

    //撤销一条命令
    public void undo() {
      if(!commands.isEmpty()) {
          commands.pop();
      }
    }
}

 定制化一个简单的Canvas类:

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

public class DrawCanvas extends Canvas implements Drawable, MouseMotionListener {

    private Color color = Color.red;

    private int radius = 3;

    private MacroCommand history;

    public DrawCanvas(int width, int height, MacroCommand command) {
        setSize(width, height);
        setBackground(Color.GRAY);
        this.history = command;

        this.addMouseMotionListener(this);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        history.execute();
    }

    @Override
    public void draw(int x, int y) {
        Graphics g = getGraphics();
        g.setColor(color);
        g.fillOval(x, y, radius, radius);
    }
    //鼠标在Cancas上拖动时,绘制图形
    @Override
    public void mouseDragged(MouseEvent e) {
        Command cmd = new DrawCommand(this, e.getPoint());
        history.append(cmd);
        cmd.execute();
    }

    @Override
    public void mouseMoved(MouseEvent e) {

    }
}

 定义一个简单的UI类:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Main extends JFrame implements ActionListener, WindowListener {

    private MacroCommand histroy = new MacroCommand();

    private DrawCanvas canvas = new DrawCanvas(400, 400, histroy);

    private JButton clearButton = new JButton("clear");
    private JButton undoButton = new JButton("undo");

    public Main(String title) {
        super(title);

        this.addWindowListener(this);
        clearButton.addActionListener(this);
        undoButton.addActionListener(this);

        Box buttonBox = new Box(BoxLayout.X_AXIS);
        buttonBox.add(clearButton);
        buttonBox.add(undoButton);

        canvas.setBackground(Color.GRAY);

        Box mainBox = new Box(BoxLayout.Y_AXIS);
        mainBox.add(canvas);
        mainBox.add(buttonBox);
        getContentPane().add(mainBox);

        pack();
        show();

    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == clearButton) {
            histroy.clear();
            canvas.repaint();
        }
        else if(e.getSource() == undoButton) {
            histroy.undo();
            canvas.repaint();
        }
    }

    @Override
    public void windowOpened(WindowEvent e) {

    }

    @Override
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }

    @Override
    public void windowClosed(WindowEvent e) {

    }

    @Override
    public void windowIconified(WindowEvent e) {

    }

    @Override
    public void windowDeiconified(WindowEvent e) {

    }

    @Override
    public void windowActivated(WindowEvent e) {

    }

    @Override
    public void windowDeactivated(WindowEvent e) {

    }


    public static void main(String[] args) {
        new Main("Command Pattern Sample");
    }
}

 看个效果,虽然有点丑陋:

这里写图片描述

 示例角色分析:

  • DrawableCancas充当Invoker角色和Client角色,因为它存了MacroCommand, 并在mouseDragged方法中调用了command的execute角色。
  • DrawCommand和MacroCommand充当ConcreteCommand角色
  • DrawCanvas也充当了receiver角色。

4. 总结

  命令模式特点:

  • 行为请求者(Invoker)与行为实现者(Receiver)分离, 媒介是Command角色。 (个人理解)。
  • 降低耦合性,但是可能会产生较多的命令类。
  • JDK的Runnable与Thread就运用了命令模式,Thread是Invoker角色, Runnable是command角色,但是我们在实现Runnable接口时直接替换了Receiver角色。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
命令模式是一种行为型设计模式,它将请求封装成一个对象,从而使得请求的发送者和接收者解耦。在命令模式,请求以命令的形式包裹在对象,并传递给调用对象。调用对象寻找可以处理该命令的合适的对象,并将命令传递给相应的对象,该对象执行命令。 在C语言,可以使用函数指针来实现命令模式。具体步骤如下: 1. 定义一个命令接口,该接口包含一个执行命令的方法。 2. 创建具体的命令类,实现命令接口,并在执行方法调用相应的函数。 3. 创建一个调用者类,该类包含一个命令对象,并提供一个执行命令的方法。 4. 在调用者类,将命令对象传递给相应的对象,并调用命令对象的执行方法。 下面是一个简单的示例代码: ```c #include <stdio.h> // 定义命令接口 typedef struct { void (*execute)(void); } Command; // 创建具体的命令类 typedef struct { Command command; void (*function)(void); } ConcreteCommand; void concreteCommand_execute(void) { printf("执行具体的命令\n"); } // 创建调用者类 typedef struct { Command *command; void (*setCommand)(Command *command); void (*executeCommand)(void); } Invoker; void invoker_setCommand(Command *command) { Invoker *invoker = (Invoker *)command; invoker->command = command;} void invoker_executeCommand(void) { Invoker *invoker = (Invoker *)invoker->command; invoker->command->execute(); } int main() { // 创建具体的命令对象 ConcreteCommand concreteCommand; concreteCommand.command.execute = concreteCommand_execute; concreteCommand.function = concreteCommand_execute; // 创建调用者对象 Invoker invoker; invoker.setCommand((Command *)&concreteCommand); invoker.executeCommand(); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值