命令模式是行为型设计模式之一。命令模式相对于其他的设计模式来说并没有那么多的条条框框,其实它不是一个很“规矩”的设计模式,就是因为这一点,命令模式相对于其他的设计模式更为灵活多变。
命令模式的定义
将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
命令模式的使用场景
需要抽象出待执行的动作,然后以参数的形式提供出来—类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品。
在不同的时刻指定、排序和执行请求。一个命令对象可以有与初始请求无关的生存期。
需要支持取消操作。
支持修改日志功能,这样当系统崩溃时,这些修改可以被重新做一遍。
需要支持事务操作。
UML类图
由图可以看出命令模式的通用代码:
/**
* Created by jmfstart on 2017/6/1.
*/
//接收者类
public class Receiver {
public void action(){
System.out.println("接受者类执行具体操作");
}
}
//抽象命令接口
public interface Command {
void execute();
}
//具体命令类
public class ConcreteCommand implements Command {
private Receiver receiver;//持有一个对接收者对象的引用
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
//调用接收者的相关方法来执行具体逻辑
receiver.action();
}
}
//请求者类
public class Invoker {
private Command command; // 持有一个对相应命令对象的引用
public Invoker(Command command) {
this.command = command;
}
public void action(){
// 调用具体命令对象的相关方法,执行具体命令
command.execute();
}
}
//客户端
public class Client {
public static void main(String[] args){
//构造一个接收者对象
Receiver receiver = new Receiver();
//根据接收者对象构造一个命令对象
ConcreteCommand command = new ConcreteCommand(receiver);
//根据具体的对象构造请求者对象
Invoker invoker = new Invoker(command);
// 执行请求方法
invoker.action();
}
}
角色介绍:
抽象命令(Command):定义命令的接口,声明执行的方法。
具体命令(ConcreteCommand):具体命令,实现要执行的方法,它通常是“虚”的实现;通常会有接收者,并调用接收者的功能来完成命令要执行的操作。
接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要能实现命令要求实现的相应功能。
调用者(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
客户端(Client):命令由客户端来创建,并设置命令的接收者。
命令模式代码的简单实现:
//命令者抽象 定义执行方法
public interface Command {
/**
* 执行命令的方法
*/
void execute();
}
//具体命令者 向左、右、掉落、变换
public class LeftCommand implements Command {
//持有一个接受者俄罗斯方块游戏对象的引用
private TetrisMachine machine;
public LeftCommand(TetrisMachine machine) {
this.machine = machine;
}
@Override
public void execute() {
//调用游戏机里的具体方法执行操作
machine.toLeft();
}
}
//具体命令者 向左、右、掉落、变换
public class RightCommand implements Command {
//持有一个接受者俄罗斯方块游戏对象的引用
private TetrisMachine machine;
public RightCommand(TetrisMachine machine) {
this.machine = machine;
}
@Override
public void execute() {
//调用游戏机里的具体方法执行操作
machine.toRight();
}
}
//具体命令者 向左、右、掉落、变换
public class FallCommand implements Command {
//持有一个接受者俄罗斯方块游戏对象的引用
private TetrisMachine machine;
public FallCommand(TetrisMachine machine) {
this.machine = machine;
}
@Override
public void execute() {
//调用游戏机里的具体方法执行操作
machine.fastToBottom();
}
}
//具体命令者 向左、右、掉落、变换
public class TransformCommand implements Command {
//持有一个接受者俄罗斯方块游戏对象的引用
private TetrisMachine machine;
public TransformCommand(TetrisMachine machine) {
this.machine = machine;
}
@Override
public void execute() {
//调用游戏机里的具体方法执行操作
machine.transform();
}
}
//请求者类,命令由按钮发起
public class Buttons {
private LeftCommand leftCommand;// 向左移动的命令对象引用
private RightCommand rightCommand;// 向右移动的命令对引用
private FallCommand fallCommand;// 向下加速的命令对象引用
private TransformCommand transformCommand;// 变换形状的命令对象引用
public void setLeftCommand(LeftCommand leftCommand) {
this.leftCommand = leftCommand;
}
public void setRightCommand(RightCommand rightCommand) {
this.rightCommand = rightCommand;
}
public void setFallCommand(FallCommand fallCommand) {
this.fallCommand = fallCommand;
}
public void setTransformCommand(TransformCommand transformCommand) {
this.transformCommand = transformCommand;
}
/**
* 按下按钮向左移动
*/
public void toLeft(){
leftCommand.execute();
}
/**
* 按下按钮向右移动
*/
public void toRight(){
rightCommand.execute();
}
/**
* 按下按钮快速落下
*/
public void fall(){
fallCommand.execute();
}
/**
* 按下按钮改变形状
*/
public void transform(){
transformCommand.execute();
}
}
//客户端
public class Player {
public static void main(String[] args){
//首先要有俄罗斯方块游戏
TetrisMachine machine = new TetrisMachine();
// 根据游戏我们构造4种命令
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.fall();
buttons.transform();
}
}
总结
模式分析
本质:对命令进行封装,将发出命令与执行命令的责任分开。
每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
请求方和接收方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
使请求本身成为一个对象,这个对象和其它对象一样可以被存储和传递。
命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。
优点
解除了请求者与实现者之间的耦合,降低了系统的耦合度。
对请求排队或记录请求日志,支持撤销操作。
可以容易地设计一个组合命令。
新命令可以容易地加入到系统中。
缺点
因为针对每一个命令都需要设计一个具体命令类,使用命令模式可能会导致系统有过多的具体命令类。