设计模式之----命令模式

设计模式之命令模式

  • 命令模式概念
  • 命令模式实践以及优缺点
  • 命令模式案例

命令模式概念

  • 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令;总之就是 : 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
  • 将请求、命令、动作等封装成对象,这样可以让项目使用这些对象来参数化其他对象,使得命令的请求者和执行者解耦

命令模式实践以及优缺点

  • 何时使用:在某些场合,比如要对行为进行”记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将”行为请求者”与”行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
  • 如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
  • 优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
  • 缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
  • 相关用途: 1.宏命令模式:命令模式 加 组合模式,我们可以将多个命令组合到一起来实现命令的批处理。;2.队列请求:将命令排成一个队列打包,一个个调用 execute 方法,如线程池的任务队列,线程不关心任务队列中是读 IO 还是计算,只取出命令后执行,接着进行下一个;3.日志请求:某些应用需要我们将所有的动作记录在日志中,然后在系统死机等情况出现时,重新调用这些动作恢复到之前的状态。如数据库事务。

命令模式案例

设使用遥控器(多排开关(没排控制一个家电功能),每排开关上面有on和off两个按钮,分别代表的是打开和关闭)来控制各种家电。
这里写图片描述
这里写图片描述

传统的设计方案

这里写图片描述

package command.bad;
/**
 * 控制的灯
 */
public class Light {

    String loc = "";//表示的是哪里的灯,比如卧室的灯,厨房的灯

    public Light(String loc) {
        this.loc = loc;
    }


    //打开某个灯
    public void on(){
        System.out.println(loc + " On!");
    }

    //关闭某个灯

    public void off(){
        System.out.println(loc + " Off!");
    }
}
package command.bad;
/**
 * 控制的音响
 */
public class Stereo {

    public int volume;//这个是设置音响的音量

    public Stereo(int volume) {
        this.volume = volume;
    }

    public void on(){
        System.out.println("Stereo on!");
    }

    public void off(){
        System.out.println("stereo off!");
    }

    public void setCd(){ //设置歌曲
        System.out.println("Stereo setCd!");
    }


    public void setVolume(int volume){
        this.volume = volume;
        System.out.println("Stereo volume = " + this.volume);
    }

    public int getVolume(){
        return this.volume;
    }

    public void start(){
        System.out.println("Stereo start!");
    }

}

遥控器接口

package command.bad;
/**
 * 这个是控制器的接口
 */
public interface Control {
    //slot是槽的意思  就是一排接口有一些槽,这里编号从0开始
    public void onButton(int slot);
    public void offButton(int slot);
}
package command.bad;

/**
 * 一个传统的遥控器的设计方案
 */
public class TraditionControl implements Control {

    private Light light;
    private Stereo stereo;//两个遥控器要控制的家电

    public TraditionControl(Light light, Stereo stereo) {
        this.light = light;
        this.stereo = stereo;
    }

    @Override
    public void onButton(int slot) {
        switch (slot){
            case 0: light.on();break;//0行插槽
            case 1: stereo.on();break; //1行插槽 控制音响的开关
            case 2:   //控制音响音量
                int vol = stereo.getVolume();
                if(vol < 11) stereo.setVolume(++vol);
                break;
        }
    }

    @Override
    public void offButton(int slot) {
        switch (slot){
            case 0: light.off();break;//0行插槽
            case 1: stereo.off();break; //1行插槽 控制音响的开关
            case 2:   //控制音响音量
                int vol = stereo.getVolume();
                if(vol > 0) stereo.setVolume(--vol);
                break;
        }
    }
}

测试类

package command.bad;

public class MyTest {

    public static void main(String[] args) {
        Light light = new Light("Bedroom");
        Stereo stereo = new Stereo(0); //一开始0音量
        Control control = new TraditionControl(light,stereo);


        control.onButton(0); //打开第一排的on按钮
        control.offButton(0);

        //第二排的
        control.onButton(1);
        control.offButton(1);

        //第三排的
        control.onButton(2);
        control.offButton(2);
    }
}

测试效果:
这里写图片描述

使用命令模式设计方案

这里写图片描述
主要是通过命令对象(里面有excute函数和undo函数),将遥控器和具体设备解耦合

这里写图片描述

首先看command包中类,从上到下:
Command类:

package command.good.command;

/**
 * 命令的接口  等下所有的Command都要实现这个接口并重写方法
 */
public interface Command {

    public void excute(); //执行命令

    public void undo(); //回退 遥控器按错了,回退
}

LightOffCommand类:

package command.good.command;

import command.good.furniture.Light;

/**
 * 关闭灯的命令
 */
public class LightOffCommand implements Command{


    private Light light;

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

    @Override
    public void excute() {
        light.off();
    }

    @Override
    public void undo() {
        light.on(); //原来是关掉的,现在就是打开
    }
}

LightOnCommand类:

package command.good.command;

import command.good.furniture.Light;

/**
 * 电灯开  的接口
 */
public class LightOnCommand implements Command{

    private Light light ; //哪个电灯

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

    @Override
    public void excute() {
        light.on();   //直接打开
    }

    @Override
    public void undo() {
        light.off(); //原来是打开的就是关掉
    }
}

MarcoCommand类(可以控制多个家具):

package command.good.command;


/**
 * 宏命令  一个命令可以控制多个 家具
 */
public class MarcoCommand implements Command{

    private Command[] commands;

    public MarcoCommand(Command[] commands) {
        this.commands = commands;
    }

    @Override
    public void excute() {
        for(int i = 0; i < commands.length; i++){
            commands[i].excute();
        }
    }

    @Override
    public void undo() {
        for(int i = commands.length - 1; i >= 0; i--){
            commands[i].undo();
        }
    }
}

NoCommand类(初始化的时候方便):

package command.good.command;

/**
 * 这个就是  啥也不做  很有用   初始化的时候
 * 并不是每一个按钮都对应着家电,有可能是空的,这样下面就不要判断是不是空了
 */
public class NoCommand implements Command{
    @Override
    public void excute() {

    }

    @Override
    public void undo() {

    }
}

StereoAddVolCommand类:

package command.good.command;

import command.good.furniture.Stereo;

/**
 * 加音量命令
 */
public class StereoAddVolCommand implements Command{

    private Stereo stereo;

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

    @Override
    public void excute() {
        int vol = stereo.getVolume();
        if(vol < 11) stereo.setVolume(++vol);
    }

    @Override
    public void undo() {
        int vol = stereo.getVolume();
        if(vol > 0) stereo.setVolume(--vol);
    }
}

StereoOffCommand类:

package command.good.command;

import command.good.furniture.Stereo;


/**
 * 音响的 关闭
 */
public class StereoOffCommand implements Command{

    private Stereo stereo;

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

    @Override
    public void excute() {
        stereo.off();
    }

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

StereoOnCommand类:

package command.good.command;

import command.good.furniture.Stereo;

/**
 * 音响的打开和 选CD
 */
public class StereoOnCommand  implements Command{
    private Stereo stereo;


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

    @Override
    public void excute() {
        stereo.on();
        stereo.setCd();
    }

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

StereoSubVolCommand类:

package command.good.command;

import command.good.furniture.Stereo;

public class StereoSubVolCommand implements Command{

    private Stereo stereo;

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

    @Override
    public void excute() {
        int vol = stereo.getVolume();
        if(vol > 0)stereo.setVolume(--vol);
    }

    @Override
    public void undo() {
        int vol = stereo.getVolume();
        if(vol < 11) stereo.setVolume(++vol);
    }
}

再看control包,主要放的是遥控类的接口和遥控器,里面有三个方法:

package command.good.control;


/**
 * 这个是控制器的接口
 */
public interface Control {
    //slot是槽的意思  就是一排接口有一些槽,这里编号从0开始
    public void onButton(int slot);
    public void offButton(int slot);

    public void undoButton(); //回退

}
package command.good.control;

import command.good.command.Command;
import command.good.command.NoCommand;

import java.util.Stack;

/**
 * 遥控器
 */
public class ModelControl implements Control{
    private Command[] onCommands; //一列的 开启按钮
    private Command[] offCommands; //一列 关闭按钮

    //为了回退  先进后出
    private Stack<Command>stack = new Stack<Command>();


    public ModelControl() {
        //初始化
        offCommands = new Command[10]; //10排
        onCommands = new Command[10];

        //下面就是NoCommand的作用
        //并不是每一个按钮都对应着家电,有可能是空的,这样下面就不要判断是不是空了
        Command noCommand = new NoCommand();
        for(int i = 0; i < onCommands.length; i++){
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }


    //遥控器并不知道绑定的是什么家具  解耦合
    //把命令对象设置到遥控器上  很重要   把命令封装成类 作为参数   命令传进来,绑定到某个插槽
    public void setCommond(int slot,Command onCommand,Command offCommand){
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }


    @Override
    public void onButton(int slot) {
        onCommands[slot].excute();
        stack.push( onCommands[slot]);
    }

    @Override
    public void offButton(int slot) {
        offCommands[slot].excute();
        stack.push(offCommands[slot]);
    }


    //具体的回退   要回退的话,首先要记住按了哪些按钮, 可以使用栈的结构
    @Override
    public void undoButton() {
        stack.pop().undo();   //回退
    }
}

接下来就是两个家具,和第一个方案一样:

package command.good.furniture;


/**
 * 控制的灯
 */
public class Light {


    String loc = "";//表示的是哪里的灯,比如卧室的灯,厨房的灯

    public Light(String loc) {
        this.loc = loc;
    }


    //打开某个灯
    public void on(){
        System.out.println(loc + "  On!");
    }

    //关闭某个灯

    public void off(){
        System.out.println(loc + "  Off!");
    }


}
package command.good.furniture;

/**
 * 控制的音响
 */
public class Stereo {

    public int volume;//这个是设置音响的音量

    public Stereo(int volume) {
        this.volume = volume;
    }

    public void on(){
        System.out.println("Stereo On!");
    }

    public void off(){
        System.out.println("stereo Off!");
    }

    public void setCd(){ //设置歌曲
        System.out.println("Stereo setCd!");
    }


    public void setVolume(int volume){
        this.volume = volume;
        System.out.println("Stereo volume = " + this.volume);
    }

    public int getVolume(){
        return this.volume;
    }

    public void start(){
        System.out.println("Stereo start!");
    }

}

然后就是测试类:

package command.good.test;

import command.good.command.*;
import command.good.control.ModelControl;
import command.good.furniture.Light;
import command.good.furniture.Stereo;

public class MyTest {
    public static void main(String[] args) {
        ModelControl control = new ModelControl(); //遥控器

        //家具
        //灯
        Light bedRoomlight = new Light("BedRoom"); //卧室的灯
        Light kitchenLight = new Light("Kitchen");//厨房的灯
        //音响
        Stereo stereo = new Stereo(0);

        //控制卧室的开的和关的
        LightOnCommand bedLightComOn = new LightOnCommand(bedRoomlight);
        LightOffCommand bedLightComOff = new LightOffCommand(bedRoomlight);


        //控制厨房的开的和关的
        LightOnCommand kitchLightComOn = new LightOnCommand(kitchenLight);
        LightOffCommand kitchLightComOff = new LightOffCommand(kitchenLight);


        //音响的开关
        StereoOnCommand stereoComOn = new StereoOnCommand(stereo);
        StereoOffCommand stereoComOff = new StereoOffCommand(stereo);

        //音响的音量调整
        StereoAddVolCommand stereoComAdd = new StereoAddVolCommand(stereo);
        StereoSubVolCommand stereoComSub = new StereoSubVolCommand(stereo);


        //给遥控器设置
        control.setCommond(0,bedLightComOn,bedLightComOff);
        control.setCommond(1,kitchLightComOn,kitchLightComOff);
        control.setCommond(2,stereoComOn,stereoComOff);
        control.setCommond(3,stereoComAdd,stereoComSub);


        //卧室灯开和关
        control.onButton(0);
        control.offButton(0);

        //厨房灯开和关
        control.onButton(1);
        control.offButton(1);

        //音响开和音量+
        control.onButton(2);
        control.onButton(3);

        //音响关和音量-
        control.offButton(3);
        control.offButton(2);

        System.out.println("----------test undo---------");

        //test undo
        control.onButton(2);
        control.onButton(3);
        control.undoButton();
        control.undoButton();


        System.out.println("----------按下一个按钮,可以控制多个设备--------");


        Command[] onCommonds = {bedLightComOn,kitchLightComOn};
        Command[] offComonds = {bedLightComOff,kitchLightComOff};
        MarcoCommand onMarco = new MarcoCommand(onCommonds);
        MarcoCommand offMarco = new MarcoCommand(offComonds);

        //放在4号插头
        control.setCommond(4,onMarco,offMarco);
        control.onButton(4);  //两个灯一起开
        control.offButton(4); //两个灯一起关
        control.undoButton();  //两个灯的操作都要回退

    }
}

测试结果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/zxzxzx0119/article/details/81607316
今日推荐