1.这一节我们的任务是创建一个类似智能家居的万能遥控器,控制各种家电。我们需要将“请求”封装成对象(一个命令对象通过在特定接收者上绑定一组动作来封装请求),以便使用不同的请求、队列、或者日志来参数化其对象——这就是命令模式。

2.我们具体来看一个例子:

首先我们要完成对命令的对象封装:

public interface Command { 
    public void execute(); 
}

只有一个方法,所有的具体命令的对象都要实现这个接口,这就做到了封装,比如对于灯这个对象,

public class Light {

public Light() { 
    }

public void on() { 
        System.out.println("Light is on"); 
    }

public void off() { 
        System.out.println("Light is off"); 
    } 
}

我们可以通过上述接口封装“开灯”这个命令,这个就是所谓的命令对象,它把动作和接收者包进对象之中,只暴露一个execute方法:

public class LightOnCommand implements Command { 
    Light light; 
    public LightOnCommand(Light light) { 
        this.light = light; 
    } 
    public void execute() { 
        light.on(); 
    } 
}

而我们的遥控器对于上述封装要一无所知,这样才能做到解耦:

public class SimpleRemoteControl { 
    Command slot; 
    public SimpleRemoteControl() {} 
    public void setCommand(Command command) { 
        slot = command; 
    } 
    public void buttonWasPressed() { 
        slot.execute(); 
    } 
}

我们现在试着用一下这个遥控器,我们首先创建一个遥控器,然后创建开灯这个命令并置于其中,最后按下按键,这样,遥控器不知道是哪个对象(实际上是灯)进行了哪个动作(实际上是开灯这个动作)就可以完成请求的发出。如此一来,遥控器和灯之间的耦合性就非常低了:

public class RemoteControlTest { 
    public static void main(String[] args) { 
        SimpleRemoteControl remote = new SimpleRemoteControl(); 
        Light light = new Light(); 
        LightOnCommand lightOn = new LightOnCommand(light); 
        remote.setCommand(lightOn); 
        remote.buttonWasPressed(); 
        } 
}

很简单的,我们想要在一个遥控机中实现控制多个家电的能力就可以用一个数组来维护这样的一组命令,可以看做提供了多个命令的插槽:

public class RemoteControl { 
  Command[] onCommands; 
Command[] offCommands; 
    public RemoteControl() { 
        onCommands = new Command[7]; 
        offCommands = new Command[7]; 
        Command noCommand = new NoCommand(); 
        for (int i = 0; i < 7; i++) { 
            onCommands[i] = noCommand; 
            offCommands[i] = noCommand; 
        } 
    } 
    public void setCommand(int slot, Command onCommand, Command offCommand) { 
        onCommands[slot] = onCommand; 
        offCommands[slot] = offCommand; 
    } 
    public void onButtonWasPushed(int slot) { 
        onCommands[slot].execute(); 
    } 
    public void offButtonWasPushed(int slot) { 
        offCommands[slot].execute(); 
    } 
    public String toString() { 
    } 
}

你可能会注意到一个叫做noCommand的对象出现在初始化遥控器对象时。这个是个小技巧,我们并不想每次都检查某个插槽是不是都绑定了命令,那么我们就引入了这个东东,实现一个不做事情的命令:

public class NoCommand implements Command { 
    public void execute() { } 
}

这样初始化就让每一个插槽都有命令了,以后若不进一步指明命令的插槽,那么就是默认这个noCommand对象。这就是一个典型的“空对象”例子,当你不想返回一个有意义的对象时,空对象就十分有用,此时空对象可以做成判断NULL或者提示“未绑定”信息的工作。

3.我们在命令模式中也可以设置类似undo的撤销命令来撤销发出的命令请求:

public interface Command { 
    public void execute(); 
    public void undo(); 
}

我们还是拿开电灯这个操作来说明,毕竟这足够简单:对于一个开灯命令,其对应的撤销命令自然是“关电灯”:

public class LightOnCommand implements Command { 
    Light light; 
    public LightOnCommand(Light light) { 
        this.light = light; 
    } 
    public void execute() { 
        light.on(); 
    } 
public void undo() { 
        light.off(); 
    } 
}

虽然这简单之极,这还没完,我们必须让遥控器记住上次到底做了什么操作,才可能去撤销:

package headfirst.command.undo;

import java.util.*;

// 
// This is the invoker 
// 
public class RemoteControlWithUndo { 
    Command[] onCommands; 
    Command[] offCommands; 
Command undoCommand; 
    public RemoteControlWithUndo() { 
        onCommands = new Command[7]; 
        offCommands = new Command[7]; 
        Command noCommand = new NoCommand(); 
        for(int i=0;i<7;i++) { 
            onCommands[i] = noCommand; 
            offCommands[i] = noCommand; 
        } 
        undoCommand = noCommand; 
    } 
    public void setCommand(int slot, Command onCommand, Command offCommand) { 
        onCommands[slot] = onCommand; 
        offCommands[slot] = offCommand; 
    } 
    public void onButtonWasPushed(int slot) { 
        onCommands[slot].execute(); 
undoCommand = onCommands[slot];//记录操作动作 
    } 
    public void offButtonWasPushed(int slot) { 
        offCommands[slot].execute(); 
undoCommand = offCommands[slot];//记录操作动作 
    } 
    public void undoButtonWasPushed() { 
undoCommand.undo();//发出撤销命令 
    } 
    public String toString() { 
    } 
}

对于电灯,我们只有两种状态,但是要是多个状态呢?我们举一个吊扇的例子,吊扇有多个转速,高中低关,四个状态:

public class CeilingFan { 
    String location = ""; 
    int level; 
    public static final int HIGH = 2; 
    public static final int MEDIUM = 1; 
    public static final int LOW = 0; 
    public CeilingFan(String location) { 
        this.location = location; 
    } 
    public void high() { 
        // turns the ceiling fan on to high 
        level = HIGH; 
        System.out.println(location + " ceiling fan is on high"); 
    }

public void medium() { 
        // turns the ceiling fan on to medium 
        level = MEDIUM; 
        System.out.println(location + " ceiling fan is on medium"); 
    }

public void low() { 
        // turns the ceiling fan on to low 
        level = LOW; 
        System.out.println(location + " ceiling fan is on low"); 
    } 
    public void off() { 
        // turns the ceiling fan off 
        level = 0; 
        System.out.println(location + " ceiling fan is off"); 
    } 
    public int getSpeed() { 
        return level; 
    } 
}

那么在实现风扇各个转速命令时,就要去记录在执行这个命令前风扇的转速,其撤销命令中则根据记录的部分完成undo操作。

public class CeilingFanHighCommand implements Command { 
    CeilingFan ceilingFan; 
    int prevSpeed;

public CeilingFanHighCommand(CeilingFan ceilingFan) { 
        this.ceilingFan = ceilingFan; 
    } 
    public void execute() { 
prevSpeed = ceilingFan.getSpeed(); 
        ceilingFan.high(); 
    } 
    public void undo() { 
        switch (prevSpeed) { 
            case CeilingFan.HIGH:     ceilingFan.high(); break; 
            case CeilingFan.MEDIUM: ceilingFan.medium(); break; 
            case CeilingFan.LOW:     ceilingFan.low(); break; 
            default:                 ceilingFan.off(); break; 
        } 
    } 
}

当然你也可以实现诸如多个命令批量执行和完成多个撤销操作。在这个例子中,还可以将电灯和电风扇等抽象为电器这个抽象类,然后启开关等两种电器的通用操作就可以写一个命令类完成,而不需要单独写“开电灯”“开电扇”这样的针对特定电器的命令类了。

命令模式可以用于工作队列和日志操作等方面。

本文转自gnuhpc博客园博客,原文链接:http://www.cnblogs.com/gnuhpc/archive/2012/12/21/2827482.html,如需转载请自行联系原作者

【HeadFirst 设计模式学习笔记】6 命令模式相关推荐

  1. 【转】设计模式学习笔记之命令模式

    定义:将请求封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式支持可撤销等操作. 命令模式将发出请求的对象和执行请求的对象解耦.在被解耦的两者之间是通过命令对象进行沟通的. 案例代 ...

  2. 设计模式学习笔记——解释器(Interpreter)模式

    设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...

  3. 设计模式学习笔记——代理(Proxy)模式

    设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...

  4. 设计模式学习笔记——状态(State)模式框架

    设计模式学习笔记--状态(State)模式框架 @(设计模式)[设计模式, 状态模式, State] 设计模式学习笔记状态State模式框架 基本介绍 状态案例 类图 实现代码 State接口 Day ...

  5. 设计模式学习笔记——备忘录(Memento)模式

    设计模式学习笔记--备忘录(Memento)模式 @(设计模式)[设计模式, 备忘录模式, memento] 设计模式学习笔记备忘录Memento模式 基本介绍 备忘录案例 类图 实现代码 Memen ...

  6. 设计模式学习笔记——观察者(Observer)模式

    设计模式学习笔记--观察者(Observer)模式 @(设计模式)[设计模式, 观察者模式, Observer] 设计模式学习笔记观察者Observer模式 基本介绍 观察者案例 类图 实现代码 Ob ...

  7. 设计模式学习笔记——外观(Facade)模式

    设计模式学习笔记--外观(Facade)模式 @(设计模式)[设计模式, 外观模式, facade] 设计模式学习笔记外观Facade模式 基本介绍 外观案例 类图 实现代码 Database类 ma ...

  8. 设计模式学习笔记——访问者(Visitor)模式

    设计模式学习笔记--访问者(Visitor)模式 @(设计模式)[设计模式, 访问者模式, visitor] 设计模式学习笔记访问者Visitor模式 基本介绍 访问者案例 类图 实现代码 Visit ...

  9. 设计模式学习笔记——装饰(Decorator)模式

    设计模式学习笔记--装饰(Decorator)模式 @(设计模式)[设计模式, 装饰模式, decorator] 设计模式学习笔记装饰Decorator模式 基本介绍 装饰案例 类图 实现代码 Dis ...

  10. 设计模式学习笔记——组合(Composite)模式

    设计模式学习笔记--组合(Composite)模式 @(设计模式)[设计模式, 组合模式, composite] 设计模式学习笔记组合Composite模式 基本介绍 组合案例 类图 实现代码 Ent ...

最新文章

  1. Python进阶02 文本文件读写
  2. Shape Drawable
  3. map for循环_Java_foundations_1 如何遍历Map
  4. Blockchain:《Blockchain applications in insurance》Deloitte—德勤区块链技术研究报告正文版—听课记录
  5. python查询sql_Python处理SQL语句(提供SQL查询平台使用)
  6. Java 在链表的开头和结尾添加元素
  7. Java对象的serialVersionUID在序列化和反序列化的用途
  8. 计算机word艺术字形状设置,4.11 Word 2016 自定义艺术字的形状效果,制作漂亮的艺术字...
  9. mysql 5.6 修改默认字符集_mysql5.6修改默认字符集
  10. [nRF51822] 1、一个简单的nRF51822驱动的天马4线SPI-1.77寸LCD彩屏DEMO
  11. qt自定义qtablemodel实现改变item的背景色
  12. 手机APP物联网远程控制开关
  13. i3cpu驱动xp_Intel英特尔Core i3/Core i5/Core i7系列CPU核芯显卡驱动
  14. FTP-文件传输协议
  15. APK Multi-Tool(反编译工具)教程
  16. ORACLE-SQL笔记
  17. idea在mac上面操作的快捷键
  18. flash保存html,如何把网页上的flash动画保存为swf格式文件(缓存提取)
  19. 数据结构-单链表基本操作带图完整详解
  20. m35c android 4.4,索尼m35c刷机的方法

热门文章

  1. mysql 主主+ Keepalived 高可用
  2. Linux下Apache虚拟主机配置
  3. python小技巧-基于python本身
  4. 强制回收和IDisposable.Dispose方法
  5. [Android] Android颜色对应的xml配置值
  6. ArcSDE建Table在ArcCatalog中不可见
  7. Silverlight专题(WatermarkedTextBox使用)--摘录 很受用
  8. Oracle访问远程服务器的数据库
  9. 【转载】VMware安装CentOS7时忘记装图形化界面——如何补装GNOME
  10. 网络编程中的锁与队列