(《设计模式解析与实战——何红辉,关爱民》读书笔记)
一、定义
将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
比如说一键装机,用户只需要动一下鼠标,它就会执行下载,装机等一系列过程。
二、使用场景
(1)需要抽象出待执行的动作,然后以参数的形式提出来——类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品;
(2)在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期;
(3)需要支持取消操作;
(4)支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍;
(5)需要支持事物操作。
三、命令模式的通用模式代码
/**
* 接收者类:执行具体逻辑的角色
*/
public class Receiver {
/**
* 真正执行具体命令逻辑的方法
*/
public void action() {
System.out.println("执行具体操作");
}
}
/**
* 抽象命令接口:定义所有具体命令类的抽象接口
*/
public interface Command {
/**
* 执行具体操作的命令
*/
void execute();
}
/**
* 具体命令类:执行具体逻辑
*/
public class ConcreteCommand implements Command {
private Receiver mReceiver;// 持有一个对接收者对象的应用
public ConcreteCommand(Receiver receiver) {
super();
this.mReceiver = receiver;
}
@Override
public void execute() {
// 调用接收者的相关方法来执行具体逻辑
mReceiver.action();
}
}
/**
* 请求者类:调用命令对象执行具体的请求
*/
public class Invoker {
private Command mCommand; // 持有一个对相应命令对象的引用
public Invoker(Command command) {
super();
this.mCommand = command;
}
public void action() {
// 调用具体命令对象的相关方法,执行具体命令
mCommand.execute();
}
}
/**
* 客户类
*/
public class Client {
public static void main(String[] args) {
// 构造一个接收者对象
Receiver receiver = new Receiver();
// 根据接收者对象构造一个命令对象
Command command = new ConcreteCommand(receiver);
// 根据具体的对象构造一个命令对象
Invoker invoker = new Invoker(command);
// 执行请求方法
invoker.action();
}
}
运行结果:
四、命令模式的简单实现
/**
* 接收者角色 俄罗斯方块游戏
*/
public class TetrisMachine {
/**
* 真正处理“向左”操作的逻辑代码
*/
public void toLeft() {
System.out.println("向左");
}
/**
* 真正处理“向右”操作的逻辑代码
*/
public void toright() {
System.out.println("向右");
}
/**
* 真正处理“快速落下”操作的逻辑代码
*/
public void fastToBottom() {
System.out.println("快速落下");
}
/**
* 真正处理“改变形状”操作的逻辑代码
*/
public void transform() {
System.out.println("改变形状");
}
}
/**
* 命令者抽象 定义执行方法
*/
public interface Command {
/**
* 命令执行方法
*/
void execute();
}
/**
* 具体命令者 向左移的命令类
*/
public class LeftCommand implements Command {
// 持有一个接收者俄罗斯方块游戏对象的引用
private TetrisMachine mMachine;
public LeftCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 调用游戏机里的具体方法执行操作
mMachine.toLeft();
}
}
/**
* 具体命令者 向右移的命令类
*/
public class RightCommand implements Command {
// 持有一个接收者俄罗斯方块游戏对象的引用
private TetrisMachine mMachine;
public RightCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 调用游戏机里的具体方法执行操作
mMachine.toright();
}
}
/**
* 具体命令者 快速落下的命令类
*/
public class FallCommand implements Command {
// 持有一个接收者俄罗斯方块游戏对象的引用
private TetrisMachine mMachine;
public FallCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 调用游戏机里的具体方法执行操作
mMachine.fastToBottom();
}
}
/**
* 具体命令者 改变形状的命令类
*/
public class TransformCommand implements Command {
// 持有一个接收者俄罗斯方块游戏对象的引用
private TetrisMachine mMachine;
public TransformCommand(TetrisMachine machine) {
super();
this.mMachine = machine;
}
@Override
public void execute() {
// 调用游戏机里的具体方法执行操作
mMachine.transform();
}
}
/**
* 请求者类 命令由按钮发起
*/
public class Buttons {
// 向左移动的命令对象引用
private LeftCommand mLeftCommand;
// 向右移动的命令对象引用
private RightCommand mRightCommand;
// 快速落下的命令对象引用
private FallCommand mFallCommand;
// 变换形状的命令对象引用
private TransformCommand mTransformCommand;
/**
* 设置向左移动的命令对象
*
* @param leftCommand
*/
public void setLeftCommand(LeftCommand leftCommand) {
this.mLeftCommand = leftCommand;
}
/**
* 设置向右移动的命令对象
*
* @param rightCommand
*/
public void setRightCommand(RightCommand rightCommand) {
this.mRightCommand = rightCommand;
}
/**
* 设置快速落下的命令对象
*
* @param fallCommand
*/
public void setFallCommand(FallCommand fallCommand) {
this.mFallCommand = fallCommand;
}
/**
* 设置变换形状的命令对象
*
* @param transformCommand
*/
public void setTransformCommand(TransformCommand transformCommand) {
this.mTransformCommand = transformCommand;
}
/**
* 按下按钮向左移动
*/
public void toLeft() {
mLeftCommand.execute();
}
/**
* 按下按钮向右移动
*/
public void toright() {
mRightCommand.execute();
}
/**
* 按下按钮快速落下
*/
public void fastToBottom() {
mFallCommand.execute();
}
/**
* 按下按钮变换形状
*/
public void transform() {
mTransformCommand.execute();
}
}
/**
* 客户类
*/
public class Player {
public static void main(String[] args) {
// 构造一个俄罗斯方块游戏
TetrisMachine machine = new TetrisMachine();
// 根据游戏构造四种命令
LeftCommand leftCommand = new LeftCommand(machine);
RightCommand rightCommand = new RightCommand(machine);
FallCommand fallCommand = new FallCommand(machine);
TransformCommand transformCommand = new TransformCommand(machine);
// 按钮可以执行不同的命令
Buttons buttons = new Buttons();
buttons.setLeftCommand(leftCommand);
buttons.setRightCommand(rightCommand);
buttons.setFallCommand(fallCommand);
buttons.setTransformCommand(transformCommand);
// 具体按下哪个按钮玩家说了算
buttons.toLeft();
buttons.toright();
buttons.fastToBottom();
buttons.transform();
}
}
运行结果:
大部分开发者或许使用一下代码:
// 构造一个俄罗斯方块游戏
TetrisMachine machine = new TetrisMachine();
machine.toLeft();
machine.toright();
machine.fastToBottom();
machine.transform();
同样可以得到期望的结果,而且代码也少,逻辑简单,但是对于开发者来说是方便,如果有一天开发者不再负责这个项目,这样的逻辑留给后来者,就不会有人觉得方便了,而且设计模式有一条原则:对修改关闭,对扩展开放。
而且命令模式另一个好处是可以实现命令记录的功能,并可以在需要时恢复。
五、总结
优点:
(1)弱耦合性;
(2)灵活的控制性;
(3)更好的扩展形。
缺点:
类的膨胀,大量衍生类的创建。