乐趣区

关于设计模式:浅谈设计模式-命令模式七

浅谈设计模式 – 命令模式(七)

前言:

命令模式也是一种比拟常见的行为型模式,能够设想咱们的手机智能遥控器,通过按动按钮的模式开启各种家具,说白了,就是将一系列的申请命令封装起来,不间接调用真正执行者的办法,这样比拟好扩大。须要留神的是命令模式和策略模式类似,所以有时候可能容易弄混,这篇文章将会具体介绍命令模式

文章目标:

  1. 理解命令的模式的特点
  2. 简略比照命令模式和策略模式
  3. 命令模式的优缺点总结

什么是命令模式?

解释:把“申请”封装为对应的对象,应用不同的申请参数化对象,命令模式反对撤销撤销的操作

命令模式是一种行为型模式,实现了接口调用对象和返回对象,用命令对象作为桥梁实现调用者和具体实现者之间的解耦和交互。

命令模式的特点:

  • 将发出请求的对象和执行申请的对象解耦
  • 调用者能够自在定义命令参数进行自在的组合
  • 命令能够用来实现日志或者 事务零碎(undo 操作)

命令模式结构图:

上面依据命令模式的定义,以及下面对于命令模式的了解,构建具体的结构图

+ Client 客户端:客户端须要创立具体的命令类,并且通过发送申请给执行者调用具体的对象,发送方和接管方不存在关联,对立由命令对象进行连贯。

+ Invoker 执行者:申请的发送者,负责将申请分发给具体的命令实现类,由实现类调用理论的执行者进行执行操作

+ Command 接口:命令接口,定义命令的标准

+ ConcreteCommand 命令接口实现类:实现命令的同时组合具体对象。

+ ConcreteObject 具体实现类:定义截图的实现生产对象。

+ Receive 执行者 :申请的真正执行者,能够是任意对象,通常以 组合 模式呈现在执行者的外部

命令模式的了解

这里参考 《Head firtst 设计模式》 的案例,模仿具体的交互流程

对象村餐厅交互过程

咱们到餐厅点餐,个别会经验如下的流程

  1. 客人负责下订单,由服务员承受订单
  2. 服务器接管订单,调用订单柜台的下订单的办法,不须要关注细节
  3. 订单柜台告诉厨师进行生产
  4. 厨师生产订单物品之后,交给服务员上菜

依据下面的步骤利用伪代码的体现如下:

  • createCommandObject() 构建命令对象
  • setCommand() 传递命令
  • execute() 命令执行
  • action1()action2() 执行者理论执行

交互流程图

咱们依据下面的交互过程介绍,构建具体的交互流程图,咱们能够看到外面有角色:客人 服务员 订单柜台 厨师,他们自身并没有关联,而是通过餐厅的模式彼此产生了具体的关联,同时咱们比照下面的结构图,看下对象村餐厅对应的结构图:

上面依据结构图说一下各种角色的职责:

客人:相当于 client 客户端,负责指挥服务员进行下单的操作。

服务员:充当申请的发送者,承受客户端的申请,调用下订单的接口到具体的订单柜台,然而不须要关怀具体的细节,只具备下订单这一个操作

订单柜台:通过服务员传递的订单,安顿厨师执行具体的工作

厨师:依据订单柜台的订单做菜,将后果返回给服务员(或客人)

咱们从下面的角色图再来看具体的命令模式定义,能够看到根本都是一一对应的状况。

命令模式和策略模式的比照

命令模式和策略模式的结构图有些许的相似,上面咱们来比照看一下这两张图的异同:

策略模式结构图:

命令模式结构图:

相同点:

  1. 命令模式通过定义命令标准接口,由子类实现命令的执行细节,策略同样定义策略行为同时用子类实现不同的策略性能
  2. 命令模式和策略都解耦了申请的发送者和执行者

不同点:

  1. 命令模式利用了命令组合执行对象的模式执行实现具体实现,而策略模式依附上下文对象进行切换
  2. 策略模式针对某个对象实现不同的策略成果,而命令模式关注申请发送者和实现者之间的业务解耦组合

实战

模仿场景:

​ 这次的案例还是模仿《Head First》设计模式的当中对于遥控器遥控电器的一个案例,咱们定义如下的内容:

遥控器:命令的发送方,负责依据不同的操作按钮调用不同的设施工作,生成具体的命令对象调用接口执行具体的命令

命令接口:负责定义命令的实现标准,充当遥控器外面的每一个按钮,对应都有具体的实现

命令实现类:负责实现命令的接口,同时调用具体的实现对象执行命令

实现对象:命令的真正执行者,个别夬在命令实现类的外部,比方电视,灯泡等

不实用设计模式

在不应用设计模式的状况下,咱们通常通过对象组合的模式组合不同的实体对象执行命令,上面通过一些简略的代码阐明一下设计的弊病:

// 灯泡
public class Light {public void on(){System.out.println("关上灯光");
    }

    public void off(){System.out.println("敞开灯光");
    }

}
// 电视机
public class Television {public void on(){System.out.println("关上电视");
    }

    public void off(){System.out.println("敞开电视");
    }

}
// 遥控器
public class RemoteControl {

    private Light light;

    private Television television;

    public RemoteControl(Light light, Television television) {
        this.light = light;
        this.television = television;
    }

    public void button1(){light.on();
    }

    public void button2(){television.on();
    }
}
// 单元测试
public class Main {public static void main(String[] args) {Television television = new Television();
        Light light = new Light();
        RemoteControl remoteControl = new RemoteControl(light, television);
        remoteControl.button1();
        remoteControl.button2();}
}/* 运行后果:关上灯光
关上电视
*/

从下面的简略代码能够看到,如果咱们持续减少电器,同时减少办法,不仅会导致遥控器要随着电器的改变一直改变,同时每次新增一个电器,遥控器要进行相似“注册”的行为,须要将电器 接入 到遥控器,这样显然是不合乎逻辑的,因为咱们都晓得,遥控器是单纯的指挥者,他不参加任何命令的操作细节,同时尽管真正工作的办法是具体对象的办法,然而这种模式相似将电器“塞”到了遥控器的外部执行,这样也是存在问题,咱们上面须要批改一下这种重大耦合的设计。

应用命令模式改写:

咱们依照命令模式的结构图,改写案例,咱们须要定义上面的类和对应的接口:

+ RemoteControl 遥控器
+ Command(接口) 命令标准接口,用于接入到遥控器外部
+ LightCommandConcrete 管制电器的亮灭命令实现
+ SwitchCommandConcrete 管制电器的开关命令实现
+ Light 灯泡
+ Television 电视机

首先,咱们定义命令的接口,定义接口的标准办法。而后定义实现子类实现不同命令的操作成果,在命令实现类的外部,咱们组合理论执行对象,在接口办法调用理论的对象办法,这样就做到了执行者和发送者之间的解耦。

接着,咱们改写控制器,他不在持有任何理论的对象办法,通过组合命令的接口,让客户端传入实现的性能,通过这种形式,遥控器不在须要依赖具体的电器实现调用具体方法,而是关注命令的接口办法,所有的细节都在命令的子类外部。

上面代码是按照命令模式进行的最简略的一个实现。

// 命令接口
public interface Command {

    /**
     * 接口备份
     */
    void execute();}

public class LightCommandConcrete implements Command {private Light light = new Light();

    @Override
    public void execute() {light.on();
    }
}

public class SwitchCommandConcrete implements Command{private Television television = new Television();

    @Override
    public void execute() {television.on();
    }
}

// 遥控器
public class RemoteControl {

    private Command command;

    public RemoteControl(Command command) {this.command = command;}

    public void execute(){command.execute();
    }

    public Command getCommand() {return command;}

    public void setCommand(Command command) {this.command = command;}
}

public class Main {public static void main(String[] args) {RemoteControl remoteControl = new RemoteControl(new LightCommandConcrete());
        remoteControl.execute();
        remoteControl.setCommand(new SwitchCommandConcrete());
        remoteControl.execute();}
}

通过下面的代码革新,咱们胜利下面的代码革新为命令模式的代码,应用设计模式之后,咱们将调用者和理论执行者进行理解耦,控制器不须要晓得执行的细节,只须要组合本人的命令接口,由客户端指定心愿实现的内容,执行绝对应的具体命令。

案例的额定扩大:

上面是对应案例如何进行后续的扩大,对于这部分内容文章篇幅无限,同时本着不反复造轮子的理念,请浏览 《Head First 设计模式》 对于命令模式这一个章节,同时安利一下这本书,十分通俗易懂的解说设计模式,对于集体的晋升帮忙很大。

对于下面的设计,如何退出 Undo 的操作?

Undo 是一个很常见的性能,如果想要让 Undo 的操作集成到案例外部,须要依照如下的步骤进行操作:

  1. Command 接口减少 Undo 的操作,让所有命令反对undo
  2. 在控制器记录 最初一个命令的执行对象,记录最初的操作命令,实现控制器反对 undo 操作
  3. 具体 Command 实现减少对于 undo() 办法调用,并且依据理论的组合对象调用办法
  4. 具体实现类实现 undo() 操作的具体行为成果。

如果 undo 外面,存在一些变量如何解决?

在命令的实现类外部,须要减少一个 最初变量值 的记录,用于记录以后最初一步操作的属性和变量

如何做到宏命令?

实现一个命令类,通过组合 数组 或者 堆栈 组合多个其余命令对象,通过 for 循环的模式顺次调用。

undo 也能够应用这种形式进行调用的,然而要留神 ** 调用的程序相同

命令模式的优缺点:

长处:

  • 命令模式实现了申请发送方和实现方解耦,不论是发送方还是接管方都不须要
  • 命令模式能够实现不同实现对象的自由组合,通过命令组合能够实现一连串简略性能

毛病:

  • 和策略模式相似,命令模式很容易造成子类的收缩

总结:

​ 命令模式是一种十分常见的设计模式,这种模式更多的关注点是解耦申请的发送方和实现方,命令模式在零碎设计中应用还是非常常见的,是一种值得关注的设计模式。

退出移动版