资讯专栏INFORMATION COLUMN

开发之路(设计模式六:命令模式上)

terasum / 3201人阅读

摘要:好的,我重新绘制了一张图反映命令模式如下图,流程与上图相同。感谢你看到这里,命令模式的上部分到这里就结束了,本人文笔随便,若有不足或错误之处望给予指点,度弯腰很快我会发布命令模式下的内容,生命不息,编程不止参考书籍设计模式

封装调用:将方法调用给封装起来。

这次讲的是命令模式,他的作用就是方法给封装起来,有需要的时候就调用,调用者并不需要关心它是如何实现的。

我们来看一张流程图
注:
1、订单封装了准备餐点的请求,
2、女服务员的工作是接受订单,然后调用订单的orderUp方法,女服务员并不关心订单内容是什么,她只需调用orderUp方法
3、厨师就是一个对象,他是真正知道餐点的具体内容的人,一旦女服务员调用orderUp方法,厨师就接手,实现餐点的具体方法,这里厨师和女服务员是解耦的,订单封装了餐点的细节,她只要调用每个订单的方法即可,而厨师看了订单就知道该做些什么餐点。

好的,我重新绘制了一张图反映命令模式如下图,流程与上图相同。

OK,我们基本了解了命令模式的流程怎样
下面写一个模拟遥控器打开电灯和门这么个动作的Demo例子

1、写电灯和门的类

电灯

package Entity;

public class Light {
    public Light() {
    }

    public void on() {

        System.out.println("灯亮了");
    }

    public void off() {
        System.out.println("灯灭了");
    }
}

package Entity;

public class Door {
    public Door() {
    }

    public void OpenDoor() {
        System.out.println("门开了");
    }

    public void OffDoor() {
        System.out.println("门关了");
    }
    
    public void StopDoor(){
        System.out.println("门停止了");
    }
    
    public void DoorLightOn(){
        System.out.println("门里的灯亮了");
    }
}

2、创建命令接口

package Interface;
/**
 * 命令接口
 * 
 * @author Joy
 * 
 */
public interface Command {
    // 命令执行方法
    public void execute();
}

3、实现一个打开电灯的命令

package Implements;

import Entity.Light;
import Interface.Command;

public class LightOnCommand implements Command {
    Light light;

    // 构造器中传入某个电灯类型
    // 以便让这个命令控制,然后记录在light实例变量中,
    // 当调用execute时,light会根据类型不同执行不同灯亮方法
    public LightOnCommand(Light light) {
        this.light = light;
    }

    // 执行灯亮方法
    @Override
    public void execute() {
        light.on();
    }
}

门的实现方法

package Implements;

import Entity.Door;
import Interface.Command;

public class DoorOpenCommand implements Command {
    Door door;

    public DoorOpenCommand(Door door) {
        this.door = door;
    }

    @Override
    public void execute() {
        door.OpenDoor();
    }
}

4、调用命令对象(遥控器)

package Control;

import Interface.Command;

/**
 * 简单遥控器
 *相当于调用者
 * @author Joy
 * 
 */
public class SimpleRemoteControl {
    // 命令对象类型,相当于插槽控制着一个装置
    Command slot;

    public SimpleRemoteControl() {
    }

    // 这个方法用来设置插槽控制的命令
    // 如果客户需要改变遥控器按钮的行为,可以多次调用此方法
    public void setCommand(Command command) {
        slot = command;
    }

    // 执行方法
    public void buttonWasPressed() {
        slot.execute();
    }
}

5、测试类(使用遥控器)

package TestMain;

import Control.SimpleRemoteControl;
import Entity.Door;
import Entity.Light;
import Implements.DoorOpenCommand;
import Implements.LightOnCommand;

public class LightTestMain {
    public static void main(String[] args) {
        // 遥控器相当于命令的调用者,会传入一个命令对象,可以用来发送请求
        SimpleRemoteControl remote = new SimpleRemoteControl();
        // 创建一个电灯对象,此对象也就是请求中的接收者
        Light light = new Light();
        Door door = new Door();
        // 创建打开电灯动作的类,并传入接收者(light)
        LightOnCommand lightOn = new LightOnCommand(light);
        DoorOpenCommand doorOpen = new DoorOpenCommand(door);
        // 命令传给调用者(遥控器)
        remote.setCommand(lightOn);
        // 模拟按下按钮
        remote.buttonWasPressed();
        remote.setCommand(doorOpen);
        remote.buttonWasPressed();
    }
}

效果图

这就是个基本命令模式的使用算是牛刀小试了一下,

命令模式定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志
来参数化其他对象,命令模式也支持可撤销的操作。

下面是这模式的类图

接下来继续模拟一个遥控器,只不过这次需求变得复杂了起来。
示例图如下,

每个遥控器的插槽都对应一个命令吗,这样遥控器就变为了“调用者”,当按下按钮,对应的命令对象的execute方法就会被调用,结果就是接收者(例如:电灯,天花板电扇,音响)的动作被调用。

代码开始
1、实体类

电灯

package Entity;

public class Light {
    //所处位置
    String location;

    public Light(String location) {
        this.location = location;
    }
    public void on(){
        System.out.println(location+"灯亮了");
    }
    
    public void off(){
        System.out.println(location+"灯关了");
    }
}

音响

package Entity;

/**
 * 音响类
 * 
 * @author Joy
 * 
 */
public class Stereo {
    //location 一个地点变量
    String location;

    public Stereo(String location) {
        this.location = location;
    }
    
    public void on(){
        System.out.println(location+"音响启动");
    }
    
    public void off(){
        System.out.println(location+"音响关闭");
    }
    
    public void setDVD(){
        System.out.println(location+"音响放一张DVD并播放");
    }
    
    public void setCD(){
        System.out.println(location+"音响放一张CD并播放");
    }
    public void setRadio(){
        System.out.println(location+"音响以收音机无线电形式播放");
    }
    
    public void setVolume(int volume) {        
        System.out.println(location + " 音响音量设置为 " + volume);
    }        
}

电视

package Entity;

public class TV {
    String location;
    int channel;//电视频道

    public TV(String location) {
        
        this.location = location;
    }
    
    public void TVOn(){
        System.out.println(location+"电视自动打开了");
    }
    public void TVOff(){
        System.out.println(location+"电视自动关闭了");
    }
    //电视调频道
    public void setTVChannel(int channel){
        this.channel=channel;
        System.out.println(location+"电视自动调到"+channel+"频道");
    }
}

2、创建命令接口对象

package Interface;
public interface Command {
    //执行
    public void execute();
    //撤销
    public void undo();
}

3、实现遥控器类(调用者)

package Control;

import Implements.NoCommand;
import Interface.Command;

/**
 * 实现遥控器
 * 
 * @author Joy
 */
public class RemoteControl {
    // 此时遥控器要处理7个开关控制,使用数组
    Command[] onCommands = new Command[7];
    Command[] offCommands = new Command[7];
    // 撤销当然要先知道之前的命令
    // 撤销变量,用来追踪最后被调用的命令
    Command undoCommand;

    // 初始化遥控器类,一开始都是无操作noCommand是一个无操作对象
    // 在测试输出时,没有被明确指明命令的插槽,其命令默认为noCommand对象
    public RemoteControl() {
        Command noCommand = new NoCommand();
        for (int i = 0; i < onCommands.length; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
        undoCommand = noCommand;
    }

    /**
     * 
     * @param slot
     *            :插槽的位置(类似索引值)
     * @param onCommand
     *            :开的命令
     * @param offCommand
     *            :关的命令 这些命令被记录在开关数组对应的插槽位置上,以便使用
     */
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    /**
     * 开关按钮是对应的插槽位置负责调用对应的方法 
     * 遥控器上面开关按钮的不同位置就可以控制不同类型的灯
     * undoCommand:当按下遥控器按钮时,我们取得这个命令,并记录在undoCommand里
     * @param slot
     */
    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();
    }

    // 打印每个插槽和它对应的命令
    @Override
    public String toString() {
        StringBuffer sbf = new StringBuffer();
        sbf.append("
======================遥控器======================
");
        for (int i = 0; i < onCommands.length; i++) {
            sbf.append("[插槽" + i + "]" + onCommands[i].getClass().getName()
                    + "	" + offCommands[i].getClass().getName() + "
");
        }
    
        return sbf.toString();
    }
}

4、实现各个命令(7个)

package Implements;

import Entity.Light;
import Interface.Command;

public class LightOffCommand implements Command {
    //具体对象变量
    Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();

    }

    @Override
    public void undo() {
        light.on();
        
    }
}
package Implements;

import Entity.Light;
import Interface.Command;

public class LightOnCommand implements Command {
    // 具体对象变量
    Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    // 执行打开电灯方法
    @Override
    public void execute() {
        light.on();

    }

    // 撤销操作,关闭电灯
    @Override
    public void undo() {
        light.off();

    }
}
package Implements;

import Interface.Command;

public class NoCommand implements Command {
    // 这是个无操作类,插槽内的类没有实例化是就走这个对象
    @Override
    public void execute() {

    }

    @Override
    public void undo() {
        // TODO 自动生成的方法存根
        
    }
}
package Implements;

import Entity.Stereo;
import Interface.Command;

public class StereoOffCommand implements Command {
    // 具体对象变量
    Stereo stereo;

    public StereoOffCommand(Stereo stereo) {
        this.stereo = stereo;
    }

    public void execute() {
        stereo.off();
    }

    @Override
    public void undo() {
        stereo.on();
    }
}
package Implements;

import Entity.Stereo;
import Interface.Command;

/**
 * 音响的
 * 
 * @author Joy
 * 
 */
public class StereoOnWithCDCommand implements Command {
    // 具体对象变量
    Stereo stereo;

    public StereoOnWithCDCommand(Stereo stereo) {
        this.stereo = stereo;
    }

    // 具体实现方法(方法再去调用实现方法)
    public void execute() {
        stereo.on();
        stereo.setCD();
        stereo.setVolume(11);
    }

    @Override
    public void undo() {
    stereo.off();
        
    }
}
package Implements;

import Entity.TV;
import Interface.Command;

public class TVOffCommand implements Command {
    TV tv;

    public TVOffCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.TVOff();
    }

    @Override
    public void undo() {
        tv.TVOn();
    }
}
package Implements;

import Entity.TV;
import Interface.Command;

public class TVOnCommand implements Command {
    TV tv;

    public TVOnCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.TVOn();
        tv.setTVChannel(15);

    }

    @Override
    public void undo() {
        tv.TVOff();
    }
}

5、测试类

package TestMain;

import Control.RemoteControl;
import Entity.Light;
import Entity.Stereo;
import Entity.TV;
import Implements.LightOffCommand;
import Implements.LightOnCommand;
import Implements.StereoOffCommand;
import Implements.StereoOnWithCDCommand;
import Implements.TVOffCommand;
import Implements.TVOnCommand;

public class TestMain {
    public static void main(String[] args) {
        // 实例化遥控器
        RemoteControl remoteControl = new RemoteControl();
        // 实例化需要控制对象,并传入房子位置
        Stereo stereo = new Stereo("客厅");
        Light light = new Light("客厅");
        TV tv = new TV("卧室");
        // 调用设备开关方法
        StereoOnWithCDCommand stereoOnWichCD = new StereoOnWithCDCommand(stereo);
        StereoOffCommand stereoOffWithCD = new StereoOffCommand(stereo);
        LightOnCommand lightOn = new LightOnCommand(light);
        LightOffCommand lightOff = new LightOffCommand(light);
        TVOnCommand tvOn = new TVOnCommand(tv);
        TVOffCommand tvOff = new TVOffCommand(tv);

        // 设置插槽位置(遥控器的哪个按钮对应哪个设备开关)
        remoteControl.setCommand(0, lightOn, lightOff);
        remoteControl.setCommand(3, stereoOnWichCD, stereoOffWithCD);
        remoteControl.setCommand(5, tvOn, tvOff);
        // 输出插槽位置
        System.out.println(remoteControl);
        // 按下开关
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(3);
        remoteControl.offButtonWasPushed(3);
        remoteControl.onButtonWasPushed(5);
        remoteControl.offButtonWasPushed(5);
    }
}

效果图

+1~~~~
在这个实例当中我特意预留了撤销的功能,让我们看看加上撤销功能(undo)的遥控器是怎么运行的把。
新建一个undoCommandTest类

package TestMain;

import Control.RemoteControl;
import Entity.Light;
import Implements.LightOffCommand;
import Implements.LightOnCommand;

public class undoCommandTest {
    public static void main(String[] args) {
        // 实例化遥控器
        RemoteControl remoteControl = new RemoteControl();
        // 实例化需要控制对象,并传入房子位置
        Light light = new Light("客厅");

        // 调用设备开关方法
        LightOnCommand lightOn = new LightOnCommand(light);
        LightOffCommand lightOff = new LightOffCommand(light);

        // 设置插槽位置(遥控器的哪个按钮对应哪个设备开关)
        remoteControl.setCommand(0, lightOn, lightOff);

        // 按下开关
        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        // 输出插槽位置
        System.out.println(remoteControl);
        // 撤销
        System.out.println("按下撤销按钮");
        remoteControl.undoButtonWasPushed();
        System.out.println("");
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(0);
        System.out.println(remoteControl);
        System.out.println("按下撤销按钮");
        remoteControl.undoButtonWasPushed();
    }
}

效果图

看来撤销的功能也OK,我脑中甚至浮现出在JavaWeb里这个撤销的效果了,23333。

感谢你看到这里,命令模式的上部分到这里就结束了,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~~~很快我会发布命令模式下的内容,生命不息,编程不止!

参考书籍:《Head First 设计模式》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70336.html

相关文章

  • 开发之路设计模式命令模式下)

    摘要:要点命令模式将发出请求的对象和执行请求的对象解耦。感谢你看到这里,命令模式到这里就结束了,本人文笔随便,若有不足或错误之处望给予指点,度弯腰很快我会发布下一个设计模式的内容,生命不息,编程不止 继续上部分的说 在之前的文章最后写了一个带有撤销电灯功能的遥控器功能,通常,想要实现撤销的功能,需要记录撤销之前的状态是什么,就比方说电扇,允许有多个风速状态,也允许被关闭。直接上代码。 1、风...

    CarlBenjamin 评论0 收藏0
  • Docker学习之路)用commit命令创建镜像

    摘要:邮件激活后,可以测试登录这条命令会完成登录,并将认证信息报错起来供后面使用。所以先用命令退出容器,再运行命令命令中,指定了要提交的修改过的容器的目标镜像仓库镜像名。提交的知识创建容器的镜像与容器的当前状态之间的差异部分,很轻量。 假期快要结束了,干点正事,接着Docker的学习。 构建镜像 构建镜像的两种方法: 使用docker commit 命令 使用docker build...

    KoreyLee 评论0 收藏0
  • Javag工程师成神之路(2019正式版)

    摘要:结构型模式适配器模式桥接模式装饰模式组合模式外观模式享元模式代理模式。行为型模式模版方法模式命令模式迭代器模式观察者模式中介者模式备忘录模式解释器模式模式状态模式策略模式职责链模式责任链模式访问者模式。 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 v1.1 2018-03-12 增加新技术知识、完善知识体系 v2.0 2019-02-19 结构...

    Olivia 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<