一 基本介绍
- 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象
发送请求
,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个, 我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计 - 命令模式使得请求发送者与请求接收者
消除彼此之间的耦合
,让对象之间的调用关系更加灵活,实现解耦
。 - 在命令模式中,会将一个
请求封装为一个对象
,以便使用不同参数来表示不同的请求(即命名),同时命令模式 也支持可撤销的操作 - 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执 行者)、命令(连接将军和士兵)。 Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持 有接收对象
1.1 举例
命令也可以是复杂的,就像脚本。命令就是这个写好的脚本。我们只需要调用这个脚本就可以完成复杂/可重复的操作
二 uml类图

对原理类图的说明-即(命名模式的角色及职责)
- Invoker 是
调用者角色
- Command: 是
命令
角色,需要执行的所有命令都在这里,可以是接口或抽象类
- Receiver: 接受者角色,
知道
如何实施和执行一个请求相关的操作 - ConcreteCommand: 将一个接受者对象与一个动作绑定(关联了Receiver对象),调用接受者相应的操作,
实现 execute
三 代码实现
3.1 示例说明

3.2 Command接口
1 package zyr.dp.command;
2
3 public interface Command {
4 public abstract void execute();
5 }
3.3 DrawCommand类
需要聚合接收者(Drawable)
package zyr.dp.command;
import java.awt.Point;
public class DrawCommand implements Command {
private Drawable drawable;
private Point position;
public DrawCommand(Drawable drawable,Point position){
this.drawable=drawable;
this.position=position;
}
public void execute() {
drawable.draw(position.x, position.y);
}
}
3.4 MacroCommand 类
命令的集合,包含了撤销
package zyr.dp.command;
import java.util.Iterator;
import java.util.Stack;
public class MacroCommand implements Command {
Stack commands=new Stack();
public void execute() {
Iterator it = commands.iterator();
while(it.hasNext()){
Command command=(Command)it.next();
command.execute();
}
}
public void append(Command command){
if(command!=this){
commands.add(command);
}
}
public void clear(){
commands.clear();
}
public void undo(){
if(!commands.isEmpty()){
commands.pop();
}
}
}
3.5 Drawable接口
接收者的抽象
package zyr.dp.command;
public interface Drawable {
public abstract void draw(int x,int y);
}
3.6 DrawCanvas 实现类
命令接收者
package zyr.dp.command;
import java.awt.*;
import java.util.Random;
public class DrawCanvas extends Canvas implements Drawable {
private static final long serialVersionUID = 1972130370393242746L;
private MacroCommand history;
private int radius=8;
public DrawCanvas(int width,int hieght, MacroCommand history){
setSize(width,hieght);
setBackground(Color.white);
this.history=history;
}
public void draw(int x, int y) {
Random random = new Random();
Graphics g = getGraphics();
g.setColor((random.nextBoolean())? Color.yellow : Color.MAGENTA);
g.fillOval(x-radius, y-radius, radius*2, radius*2);
}
@Override
public void paint(Graphics g) {
System.out.println("执行一次刷新!"+System.currentTimeMillis());
history.execute();
}
}
3.7 测试类
package zyr.dp.command;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.*;
public class Main extends JFrame implements ActionListener,MouseMotionListener,WindowListener{
private MacroCommand history=new MacroCommand() ;
private JButton btnClear=new JButton("清除");
private JButton btnRePaint=new JButton("重现");
private DrawCanvas canvas=new DrawCanvas(400,400,history);
public Main(String title){
super(title);
this.addWindowListener(this);
canvas.addMouseMotionListener(this);
btnClear.addActionListener(this);
btnRePaint.addActionListener(this);
Box btnBox=new Box(BoxLayout.X_AXIS);
btnBox.add(btnClear);
btnBox.add(btnRePaint);
Box mainBox=new Box(BoxLayout.Y_AXIS);
mainBox.add(btnBox);
mainBox.add(canvas);
getContentPane().add(mainBox);
pack();
show();
}
public static void main(String[] args) {
new Main("命令模式");
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnClear){
history.clear();
canvas.repaint();
}else if(e.getSource()==btnRePaint){
canvas.repaint();
}
}
@Override
public void mouseDragged(MouseEvent e) {
Command cmd=new DrawCommand(canvas,e.getPoint());
history.append(cmd);
cmd.execute();
}
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
@Override
public void windowOpened(WindowEvent e) {
}
@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) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
}
四 注意事项和细节
- 目的是解耦,不需要将命令发布者和命令执行者关联到一起
- 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
- 容易实现对请求的撤销和重做
-
命令模式不足
:可能导致某些系统有过多的具体命令
类,增加了系统的复杂度,这点在在使用的时候要注意 - 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一 个按键都要判空,这给我们编码带来一定的麻烦。
-
命令模式经典的应用场景
:界面的一个按钮都是一条命令
、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
网友评论