设计模式第六篇-命令模式

一、引言

先看需求:设计一个家电遥控器系统,每个家电由开、关两个按钮控制, 每个家电都由各自的厂商提供了实现方法,我们只需要调用这些方法即可,如图所示:

如何实现这个功能呢?

第一步我们要排除的实现方式就是if条件判断,因为一旦增加家电,我们就必须修改代码,这不符合我们的设计思路。

然后我们想想,遥控按钮只是发出一个请求,具体的实现是通过各自厂商的API,我们应该让遥控器(动作的请求者)从厂商的API(动作的执行者)中解耦出来。可是怎么去解耦呢?毕竟按钮动作和家电行为是息息相关的。

这个时候就可以使用命令模式,来认识一下命令模式。

二、命令模式

先看定义:命令模式是把一个操作或者行为抽象为一个对象中,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开。命令模式的实现可以提供命令的撤销和恢复功能

听着还是很抽象,我们再举一个例子,《破产姐妹》中熟悉的场景 ,顾客到餐厅点单,服务员MAX拿了订单,放在订单柜台,厨师Oleg根据订单准备餐点。

分析下其中的过程,把订单想象成一个用来请求准备餐点的对象,订单对象可以被传递,服务员MAX负责传递对象,订单的接口中只包含一个orderUp()方法,这个方法封装了而准备餐点所需的动作,订单内有一个“需要进行准备工作的对象”(也就是厨师Oleg)的引用,这一切都封装起来,MAX甚至不需要知道订单上有什么,她只负责传递。

有没有清楚一些,转成类图:

类图中可以看出,其中的几个角色:

  • 客户角色(Client):发出一个具体的命令并设置其接受者。
  • 调用者(Invoker):持有一个命令对象,负责命令对象执行命令。
  • 命令角色(Command):声明命令接口,并具备一个让接收者执行的方法。
  • 具体命令角色(ConreteCommand):定义了动作和接收者之间的绑定关系,负责调用接收者的方法。
  • 接受者(Receiver):负责具体动作的执行。

三、代码实现

命令接口:

//命令接口
public interface Command {
    //命令方法
    void execute();
}

命令接收者

//命令接收者(Receiver),电灯
public class Light {
    private String name;
    public Light(String name){
       this.name=name;
    }
    //开灯操作
    public void on(){
        System.out.println(name+":开灯!");
    }
    //关灯操作
    public void off(){
        System.out.println(name+":关灯");
    }
}
绑定命令与接收者关系
//绑定命令与接收者关系ConreteCommand
public class LightOnCommand implements Command {
    Light light;
    public LightOnCommand(Light light){
        this.light=light;
    }
    //具体命令方法
    public void execute() {
       light.on();
    }
}

调用者(Invoker)

//命令模式的客户(Invoker)
public class SimpleRemoteControl {
    //命令接口
    Command solt;
    public void setCommand(Command command){
        this.solt=command;
    }
    //命令方法
    public void buttonWasPressed(){
        solt.execute();
    }

}

运行:

private static void simpleControl() {
        //遥控器调用者
        SimpleRemoteControl control=new SimpleRemoteControl();
        //电灯
        Light light=new Light("客厅");
        //具体命令类
        LightOnCommand lightOnCommand=new LightOnCommand(light);
        //设置命令
        control.setCommand(lightOnCommand);
        //命令方法
        control.buttonWasPressed();

    }

结果:

这里其实只实现了其中一个按钮,让我们来补充一些代码

增加多一个接收者:

//另外一个接受者吊灯
public class CeilingFan {
    private String name;

    public CeilingFan(String name){
        this.name=name;
    }

    public void on(){
        System.out.println(name+":打开");
    }

    public void off(){
        System.out.println(name+":关闭");
    }
}
//吊灯的开灯命令
public class CeilingFanOffCommand implements Command {
    CeilingFan ceilingFan;
    public CeilingFanOffCommand(CeilingFan ceilingFan){
        this.ceilingFan=ceilingFan;
    }
    //具体命令方法
    public void execute() {
        ceilingFan.off();
    }
}

//吊灯的关灯命令
public class CeilingFanOnCommand implements Command {
    CeilingFan ceilingFan;
    public CeilingFanOnCommand(CeilingFan ceilingFan){
        this.ceilingFan = ceilingFan;
    }
    //具体命令方法
    public void execute() {
        ceilingFan.on();
    }
}

调用者遥控:

//遥控调用
public class RemoteControl {
    //申明命令数组
    Command[] onCommands;
    Command[] offCommands;
    public RemoteControl(){
        onCommands=new Command[4];
        offCommands=new Command[4];
    }
    //设置命令
    public void setCommand(int solt,Command onCommand,Command offCommand){
        onCommands[solt]=onCommand;
        offCommands[solt]=offCommand;
    }
    //打开按钮
    public void onButtonWasPressed(int solt){
         onCommands[solt].execute();
    }
    //关闭按钮
    public void offButtonWasPressed(int solt){
        offCommands[solt].execute();
    }

}

实际操作遥控:

private static void control() {
        RemoteControl remoteControl=new RemoteControl();
        Light roomLight=new Light("客厅灯");
        Light kitchLight=new Light("厨房灯");
        CeilingFan roomCeilingFan=new CeilingFan("客厅吊扇");
        CeilingFan kitchCeilingFan=new CeilingFan("厨房吊扇");
        //电灯相关命令
        LightOnCommand roomLightOnCommand=new LightOnCommand(roomLight);
        LightOnCommand kitchLightOnCommand=new LightOnCommand(kitchLight);
        LightOffCommand roomLightOffCommand=new LightOffCommand(roomLight);
        LightOffCommand kitchLightOffCommand=new LightOffCommand(kitchLight);
        //吊扇相关命令
        CeilingFanOnCommand roomCeilingFanOnCommand =new CeilingFanOnCommand(roomCeilingFan);
        CeilingFanOnCommand kitchCeilingFanOnCommand =new CeilingFanOnCommand(kitchCeilingFan);
        CeilingFanOffCommand roomCeilingFanOffCommand =new CeilingFanOffCommand(roomCeilingFan);
        CeilingFanOffCommand kitchCeilingFanOffCommand =new CeilingFanOffCommand(kitchCeilingFan);
        //将命令加载到卡槽中
        remoteControl.setCommand(0,roomLightOnCommand,roomLightOffCommand);
        remoteControl.setCommand(1,kitchLightOnCommand,kitchLightOffCommand);
        remoteControl.setCommand(2,roomCeilingFanOnCommand,roomCeilingFanOffCommand);
        remoteControl.setCommand(3,kitchCeilingFanOnCommand,kitchCeilingFanOffCommand);
        //使用遥控
        remoteControl.onButtonWasPressed(0);
        remoteControl.offButtonWasPressed(0);
        remoteControl.onButtonWasPressed(1);
        remoteControl.offButtonWasPressed(1);
        remoteControl.onButtonWasPressed(2);
        remoteControl.offButtonWasPressed(2);
        remoteControl.onButtonWasPressed(3);
        remoteControl.offButtonWasPressed(3);
    }

运行结果:

四、总结

在下面的情况下可以考虑使用命令模式:

  1. 系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法吧命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。
  2. 系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。
  3. 如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。
  4. 系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法。

优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。

缺点:使用命令模式可能会导致某些系统有过多的具体命令类。

另附源码地址:https://gitee.com/yuanqinnan/pattern

原文地址:https://www.cnblogs.com/yuanqinnan/p/10175419.html

时间: 2024-10-08 17:32:49

设计模式第六篇-命令模式的相关文章

设计模式(六)——命令模式

命令模式:将"请求"封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也支持可撤销的操作. 一个命令对象通过在特定的接收者上面绑定一组动作来封装这个请求.要达到这一点,命令对象将接收者和动作封装在一个对象中,只暴露出一个Execute()方法.当此方法调用则进行接收者的动作.从外面看来,其他对象不需要知道哪个接收者进行了哪些动作,只知道如果调用了Execute()方法,请求的目的就能达到. 例如有一个简单的遥控器能够控制灯的开关: 首先定义Command接口: 1

云计算设计模式(六)——命令和查询职责分离(CQRS)模式

云计算设计模式(六)--命令和查询职责分离(CQRS)模式 隔离,通过使用不同的接口,从操作读取数据更新数据的操作.这种模式可以最大限度地提高性能,可扩展性和安全性;支持系统在通过较高的灵活性,时间的演变;防止更新命令,从造成合并在域级别上的冲突. 背景和问题 在传统的数据管理系统中,这两个命令(更新数据)和查询(请求数据),针对在一个单一的数据存储库中的相同的一组实体的执行.这些实体可以是在关系数据库中的一个或多个表,如SQL Server的行的子集. 典型地,在这些系统中,所有的创建,读取,

Java设计模式(六)合成模式 享元模式

(十一)合成模式 Composite 合成模式是一组对象的组合,这些对象可以是容器对象,也可以是单对象.组对象允许包含单对象,也可以包含其他组对象,要为组合对象和单对象定义共同的行为.合成模式的意义是 保证客户端调用单对象与组合对象的一致性. class TreeNode{ private String name; private TreeNode parent; private Vector<TreeNode> children = new Vector<TreeNode>();

[设计模式篇] 命令模式

我时常以为,所谓的“设计模式”,更多像是一种方法论,而不应该算作所谓的“知识点”.这些就像高中数学有专门的解题方法集合,形如“不等式逼等式”(证明一个未知数大于等于一个值,同时又证明这个未知数小于等于这个值,那么这个未知数只能是这个值)这样的方法一般,根本没法说得非常具体.而这才是所谓“知识”大放异彩的地方,没有这些无数人总结出来的具体的方法,让我们通过个人总结对现有的知识点进行链接来形成一套,哪怕只是一个小小的方法,或者模式,都是很难得的.如果将学高中数学的方法论做一个合理的外推,那么程序设计

Java设计模式(六) Command(命令模式)及Tomcat引申

基本概念 Command 命令模式是一个高内聚的模式(满足单一职责原则) 概念:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 命令模式在项目中频繁使用,封装性和拓展性都有很好的保障 Command模式中的角色分工: Client:创建一个命令并决定接受者 Command:命令接口,定义一个抽象方法 Concrete Command:具体命令,负责调用接受者的相关操作 Invoker:请求者,负责调用命令对象执行请求 R

设计模式(行为型)之命令模式(Command Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(行为型)之策略模式(Strategy Pattern)>http://blog.csdn.net/yanbober/article/details/45498567 概述 在软件开发中,我们经常需要向某些对象发送请求(调用其中的某个或某些方法),但是并不知道请求的接收

javascript设计模式详解之命令模式

每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某些设计模式不自觉的我们都在使用.只不过没有对应起来罢了.本文就力求以精简的语言去介绍下设计模式这个高大上的概念.相信会在看完某个设计模式之后有原来如此的感慨. 一.基本概念与使用场景: 基本概念: 将请求封装成对象,分离命令接受者和发起者之间的耦合. 命令执行之前在执行对象中传入接受者.主要目的相互

设计模式14:Command 命令模式(行为型模式)

Command 命令模式(行为型模式) 耦合与变化 耦合是软件不能抵御变化的根本性原因.不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系. 动机(Motivation) 在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”.但在某些场合——比如对行为进行“记录.撤销/重做(undo/redo).事务”等处理,这种无法抵御变化的紧耦合是不合适的. 在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的解耦. 意

设计模式学习笔记之命令模式

命令模式 将“请求”封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 说明: 1.命令模式将发出请求的对象和执行请求的对象解耦: 2.在被解耦的两者之间是通过命令对象进行沟通的.命令对象封装了接受者和一个或一组动作: 3.调用者通过调用命令对象的execute()发出请求,这会使得接受者的动作被调用: 4.调用者可以接受命令当做参数,甚至在运行时动态地进行: 5.命令可以支持撤销,做法事实现一个undo()方法来回到exexcute()被执行前的状态: