概念
定义
将一个请求封装为一个对象,从而使你可用不同请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。本质“封装请求”
结构

该模式有五个角色。“命令接口”,“命令接口实现类”,“接收者”,“要求执行者”,“装配者”
Command:定义命令接口
ConcreteCommand:命令接口实现对象,持有接收者,调用接收者的功能来完成命令操作
Receiver:接收者,真正执行命令的对象
Invoker:要求命令对象执行请求,通常持有命令对象,是装配者触发命令并执行操作的入口
Client:装配者,创建具体的命令对象,设置命令对象的接收者,用于组装命令对象和接收者
关键说明
关键在于,将请求封装成命令对象,并定义统一执行操作接口。但是传统的命令模式,真正执行功能的是接收者,其他相关都是转调方法。装配者的工作是组装,让命令模式可以成功搭建
小示例
命令接口
/**
* 命令接口,声明执行的操作
*/
public interface Command {
/**
* 执行命令对应的操作
*/
public void execute();
}
调用者
/**
* 调用者
*/
public class Invoker {
/**
* 持有命令对象
*/
private Command command = null;
/**
* 设置调用者持有的命令对象
* @param command 命令对象
*/
public void setCommand(Command command) {
this.command = command;
}
/**
* 示意方法,要求命令执行请求
*/
public void runCommand() {
//调用命令对象的执行方法
command.execute();
}
}
命令接口实现对象
/**
* 具体的命令实现对象
*/
public class ConcreteCommand implements Command {
/**
* 持有相应的接受者对象
*/
private Receiver receiver = null;
/**
* 示意,命令对象可以有自己的状态
*/
private String state;
/**
* 构造方法,传入相应的接受者对象
* @param receiver 相应的接受者对象
*/
public ConcreteCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute() {
//通常会转调接受者对象的相应方法,让接受者来真正执行功能
receiver.action();
}
}
接收者,真正执行对象
/**
* 接收者对象
*/
public class Receiver {
/**
* 示意方法,真正执行命令相应的操作
*/
public void action(){
//真正执行命令操作的功能代码
}
}
装配者
public class Client {
/**
* 示意,负责创建命令对象,并设定它的接受者
*/
public void assemble(){
//创建接受者
Receiver receiver = new Receiver();
//创建命令对象,设定它的接收者
Command command = new ConcreteCommand(receiver);
//创建Invoker,把命令对象设置进去
Invoker invoker = new Invoker();
invoker.setCommand(command);
}
}
实例
BOX是调用者,持有命令对象,转调命令对象的执行方法
OpenCommand是命令接口实例对象,持有接受者,转调接收者对象的执行方法
MainBoardApi是接收者父类,其子类真正实现功能
Client进行命令装配等等工作
调用者,命令程序入口
/**
* 机箱对象,本身有按钮,持有按钮对应的命令对象
*/
public class Box {
/**
* 开机命令对象
*/
private Command openCommand;
/**
* 设置开机命令对象
* @param command 开机命令对象
*/
public void setOpenCommand(Command command){
this.openCommand = command;
}
/**
* 提供给客户使用,接受并相应用户请求,相当于按钮被按下触发的方法
*/
public void openButtonPressed(){
//按下按钮,执行命令
openCommand.execute();
}
}
命令接口
/**
* 命令接口,声明执行的操作
*/
public interface Command {
/**
* 执行命令对应的操作
*/
public void execute();
}
开机命令实例
/**
* 开机命令的实现,实现Command接口,
* 持有开机命令的真正实现,通过调用接收者的方法来实现命令
*/
public class OpenCommand implements Command{
/**
* 持有真正实现命令的接收者——主板对象
*/
private MainBoardApi mainBoard = null;
/**
* 构造方法,传入主板对象
* @param mainBoard 主板对象
*/
public OpenCommand(MainBoardApi mainBoard) {
this.mainBoard = mainBoard;
}
public void execute() {
//对于命令对象,根本不知道如何开机,会转调主板对象
//让主板去完成开机的功能
this.mainBoard.open();
}
}
接收者父类
/**
* 主板的接口
*/
public interface MainBoardApi {
/**
* 主板具有能开机的功能
*/
public void open();
}
/**
* 微星主板类,开机命令的真正实现者,在Command模式中充当Receiver
*/
public class MsiMainBoard implements MainBoardApi{
/**
* 真正的开机命令的实现
*/
public void open(){
System.out.println("微星主板现在正在开机,请等候");
System.out.println("接通电源......");
System.out.println("设备检查......");
System.out.println("装载系统......");
System.out.println("机器正常运转起来......");
System.out.println("机器已经正常打开,请操作");
}
}
/**
* 技嘉主板类,开机命令的真正实现者,在Command模式中充当Receiver
*/
public class GigaMainBoard implements MainBoardApi{
/**
* 真正的开机命令的实现
*/
public void open(){
System.out.println("技嘉主板现在正在开机,请等候");
System.out.println("接通电源......");
System.out.println("设备检查......");
System.out.println("装载系统......");
System.out.println("机器正常运转起来......");
System.out.println("机器已经正常打开,请操作");
}
}
装配者
public class Client {
public static void main(String[] args) {
//1:把命令和真正的实现组合起来,相当于在组装机器,
//把机箱上按钮的连接线插接到主板上。
MainBoardApi mainBoard = new MsiMainBoard();
OpenCommand openCommand = new OpenCommand(mainBoard);
//真正的客户端测试
//2:为机箱上的按钮设置对应的命令,让按钮知道该干什么
Box box = new Box();
box.setOpenCommand(openCommand);
//3:然后模拟按下机箱上的按钮
box.openButtonPressed();
}
}
可撤销命令实例
可撤销操作的意思就是:放弃该操作,回到未执行该操作前的状态
可撤销模式分为补偿式,存储恢复式
补偿式:又叫反操作式,如果被撤销的操作是加的功能,那么撤销的实现就是减的功能
存储恢复式:把操作前的状态记录下来,然后要撤退操作时直接恢复回去。
补偿式:
一个加减的计算器,由撤销到上一层,也有撤销恢复的功能
调用者,程序入口
/**
* 计算器类,计算器上有加法按钮、减法按钮,还有撤销和恢复的按钮
*/
public class Calculator {
/**
* 命令的操作的历史记录,在撤销时候用
*/
private List<Command> undoCmds = new ArrayList<Command>();
/**
* 命令被撤销的历史记录,在恢复时候用
*/
private List<Command> redoCmds = new ArrayList<Command>();
private Command addCmd = null;
private Command substractCmd = null;
public void setAddCmd(Command addCmd) {
this.addCmd = addCmd;
}
public void setSubstractCmd(Command substractCmd) {
this.substractCmd = substractCmd;
}
public void addPressed(){
this.addCmd.execute();
//把操作记录到历史记录里面
undoCmds.add(this.addCmd);
}
public void substractPressed(){
this.substractCmd.execute();
//把操作记录到历史记录里面
undoCmds.add(this.substractCmd);
}
public void undoPressed(){
if(this.undoCmds.size()>0){
//取出最后一个命令来撤销
Command cmd = this.undoCmds.get(this.undoCmds.size()-1);
cmd.undo();
//如果还有恢复的功能,那就把这个命令记录到恢复的历史记录里面
this.redoCmds.add(cmd );
//然后把最后一个命令删除掉,
this.undoCmds.remove(cmd);
}else{
System.out.println("很抱歉,没有可撤销的命令");
}
}
public void redoPressed(){
if(this.redoCmds.size()>0){
//取出最后一个命令来重做
Command cmd = this.redoCmds.get(this.redoCmds.size()-1);
cmd.execute();
//把这个命令记录到可撤销的历史记录里面
this.undoCmds.add(cmd);
//然后把最后一个命令删除掉
this.redoCmds.remove(cmd);
}else{
System.out.println("很抱歉,没有可恢复的命令");
}
}
}
命令接口
/**
* 命令接口,声明执行的操作,支持可撤销操作
*/
public interface Command {
/**
* 执行命令对应的操作
*/
public void execute();
/**
* 执行撤销命令对应的操作
*/
public void undo();
}
/**
* 具体的加法命令实现对象
*/
public class AddCommand implements Command{
/**
* 持有具体执行计算的对象
*/
private OperationApi operation = null;
/**
* 操作的数据,也就是要加上的数据
*/
private int opeNum;
/**
* 构造方法,传入具体执行计算的对象
* @param operation 具体执行计算的对象
* @param opeNum 要加上的数据
*/
public AddCommand(OperationApi operation,int opeNum){
this.operation = operation;
this.opeNum = opeNum;
}
public void execute() {
//转调接收者去真正执行功能,这个命令是做加法
this.operation.add(opeNum);
}
public void undo() {
//转调接收者去真正执行功能
//命令本身是做加法,那么撤销的时候就是做减法了
this.operation.substract(opeNum);
}
}
/**
* 具体的减法命令实现对象
*/
public class SubstractCommand implements Command{
/**
* 持有具体执行计算的对象
*/
private OperationApi operation = null;
/**
* 操作的数据,也就是要减去的数据
*/
private int opeNum;
/**
* 构造方法,传入具体执行计算的对象
* @param operation 具体执行计算的对象
* @param opeNum 要减去的数据
*/
public SubstractCommand(OperationApi operation,int opeNum){
this.operation = operation;
this.opeNum = opeNum;
}
public void execute() {
//转调接收者去真正执行功能,这个命令是做减法
this.operation.substract(opeNum);
}
public void undo() {
//转调接收者去真正执行功能
//命令本身是做减法,那么撤销的时候就是做加法了
this.operation.add(opeNum);
}
}
接收者
/**
* 操作运算的接口
*/
public interface OperationApi {
/**
* 获取计算完成后的结果
* @return 计算完成后的结果
*/
public int getResult();
/**
* 设置计算开始的初始值
* @param result 计算开始的初始值
*/
public void setResult(int result);
/**
* 执行加法
* @param num 需要加的数
*/
public void add(int num);
/**
* 执行减法
* @param num 需要减的数
*/
public void substract(int num);
}
/**
* 操作运算的接口
*/
public interface OperationApi {
/**
* 获取计算完成后的结果
* @return 计算完成后的结果
*/
public int getResult();
/**
* 设置计算开始的初始值
* @param result 计算开始的初始值
*/
public void setResult(int result);
/**
* 执行加法
* @param num 需要加的数
*/
public void add(int num);
/**
* 执行减法
* @param num 需要减的数
*/
public void substract(int num);
}
装配者
public class Client {
public static void main(String[] args) {
//1:组装命令和接收者
//创建接收者
OperationApi operation = new Operation();
//创建命令对象,并组装命令和接收者
AddCommand addCmd = new AddCommand(operation,5);
SubstractCommand substractCmd = new SubstractCommand(operation,3);
//2:把命令设置到持有者,就是计算器里面
Calculator calculator = new Calculator();
calculator.setAddCmd(addCmd);
calculator.setSubstractCmd(substractCmd);
//3:模拟按下按钮,测试一下
calculator.addPressed();
System.out.println("一次加法运算后的结果为:"+operation.getResult());
calculator.substractPressed();
System.out.println("一次减法运算后的结果为:"+operation.getResult());
//测试撤消
calculator.undoPressed();
System.out.println("撤销一次后的结果为:"+operation.getResult());
calculator.undoPressed();
System.out.println("再撤销一次后的结果为:"+operation.getResult());
//测试恢复
calculator.redoPressed();
System.out.println("恢复操作一次后的结果为:"+operation.getResult());
calculator.redoPressed();
System.out.println("再恢复操作一次后的结果为:"+operation.getResult());
}
}
宏命令实例
宏命令就是包含多个命令的命令,即一个命令的组合
把一堆命令当做菜单,菜单的每个菜就是一个命令。同时把菜单抽象成一个大命令。当用户点餐完,就触发大命令,执行菜单这个命令即可。
宏命令对象
/**
* 菜单对象,是个宏命令对象
*/
public class MenuCommand implements Command {
/**
* 用来记录组合本菜单的多道菜品,也就是多个命令对象
*/
private Collection<Command> col = new ArrayList<Command>();
/**
* 点菜,把菜品加入到菜单中
* @param cmd 客户点的菜
*/
public void addCommand(Command cmd){
col.add(cmd);
}
public void execute() {
//执行菜单其实就是循环执行菜单里面的每个菜
for(Command cmd : col){
cmd.execute();
}
}
}
装配者+调用者
/**
* 服务员,负责组合菜单,负责组装每个菜和具体的实现者,
* 还负责执行调用,相当于标准Command模式的Client+Invoker
*/
public class Waiter {
/**
* 持有一个宏命令对象——菜单
*/
private MenuCommand menuCommand = new MenuCommand();
/**
* 客户点菜
* @param cmd 客户点的菜,每道菜是一个命令对象
*/
public void orderDish(Command cmd){
//客户传过来的命令对象是没有和接收者组装的
//在这里组装吧
CookApi hotCook = new HotCook();
CookApi coolCook = new CoolCook();
//判读到底是组合凉菜师傅还是热菜师傅
//简单点根据命令的原始对象的类型来判断
if(cmd instanceof DuckCommand){
((DuckCommand)cmd).setCookApi(hotCook);
}else if(cmd instanceof ChopCommand){
((ChopCommand)cmd).setCookApi(hotCook);
}else if(cmd instanceof PorkCommand){
//这是个凉菜,所以要组合凉菜的师傅
((PorkCommand)cmd).setCookApi(coolCook);
}
//添加到菜单中
menuCommand.addCommand(cmd);
}
/**
* 客户点菜完毕,表示要执行命令了,这里就是执行菜单这个组合命令
*/
public void orderOver(){
this.menuCommand.execute();
}
}
命令接口
/**
* 命令接口,声明执行的操作
*/
public interface Command {
/**
* 执行命令对应的操作
*/
public void execute();
}
/**
* 命令对象,北京烤鸭
*/
public class DuckCommand implements Command{
private CookApi cookApi = null;
public void setCookApi(CookApi cookApi) {
this.cookApi = cookApi;
}
public void execute() {
this.cookApi.cook("北京烤鸭");
}
}
/**
* 命令对象,蒜泥白肉
*/
public class PorkCommand implements Command {
private CookApi cookApi = null;
public void setCookApi(CookApi cookApi) {
this.cookApi = cookApi;
}
public void execute() {
this.cookApi.cook("蒜泥白肉");
}
}
/**
* 命令对象,绿豆排骨煲
*/
public class ChopCommand implements Command{
/**
* 持有具体做菜的厨师的对象
*/
private CookApi cookApi = null;
/**
* 设置具体做菜的厨师的对象
* @param cookApi 具体做菜的厨师的对象
*/
public void setCookApi(CookApi cookApi) {
this.cookApi = cookApi;
}
public void execute() {
this.cookApi.cook("绿豆排骨煲");
}
}
接收者
/**
* 厨师的接口
*/
public interface CookApi {
/**
* 示意,做菜的方法
* @param name 菜名
*/
public void cook(String name);
}
/**
* 厨师对象,做凉菜
*/
public class CoolCook implements CookApi {
public void cook(String name) {
System.out.println("凉菜"+name+"已经做好,本厨师正在装盘。" );
}
}
/**
* 厨师对象,做热菜
*/
public class HotCook implements CookApi{
public void cook(String name) {
System.out.println("本厨师正在做:"+name);
}
}
测试类
public class Client {
public static void main(String[] args) {
//只是负责向服务员点菜就好了
//创建服务员
Waiter waiter = new Waiter();
//创建命令对象,就是要点的菜
Command chop = new ChopCommand();
Command duck = new DuckCommand();
Command pork = new PorkCommand();
//点菜,就是把这些菜让服务员记录下来
waiter.orderDish(chop);
waiter.orderDish(duck);
waiter.orderDish(pork);
//点菜完毕
waiter.orderOver();
}
}
命令模式的相关信息
1.存在退化的命令模式,也可以叫超级智能,不需要接收者,也不需要组装者,全部功能由命令接口实例对象来完成。真正的用户,通过invoker来触发命令即可。所以退化的命令模式,只有三个角色,“命令接口”,“命令实例对象”,“调用者invoker”