欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-26 08:46:41
...

设计模式之命令模式

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

命令模式概念

  • 命令模式(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();  //两个灯的操作都要回退

    }
}

测试结果:
设计模式之----命令模式

相关标签: 命令模式