美文网首页
命令模式

命令模式

作者: Whyn | 来源:发表于2018-11-04 11:01 被阅读20次

    简介

    Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.
    将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

    命令模式(Command Pattern)是对命令的封装,每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式 解耦了请求方和接收方,请求方只需请求执行命令,不用关心命令是怎样被接收,怎样被操作以及是否被执行····

    软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。但紧耦合关系缺乏扩展性,在某些场合中,当需要为行为进行记录,撤销或重做等处理时,只能修改源码。而 命令模式 通过为请求与实现间引入一个抽象命令接口,解耦了请求与实现,并且中间件是抽象的,它可以有不同的子类实现,因此其具备扩展性。

    命令模式 本质:解耦命令请求与处理

    主要解决

    当系统的某项操作具备命令语义时,且命令实现不稳定(变化),那么可以通过 命令模式 解耦请求与实现,利用抽象命令接口使请求方代码架构稳定,封装接收方具体命令实现细节。接收方与抽象命令接口呈现弱耦合(内部方法无需一致),具备良好的扩展性。

    优缺点

    优点

    • 通过引入中间件(抽象接口),解耦了命令请求与实现;
    • 扩展性良好,可以很容易地增加新命令;
    • 支持组合命令,支持命令队列;
    • 可以在现有命令的基础上,增加额外功能(比如日志记录···,结合 装饰器模式 更酸爽);

    缺点

    • 具体命令类可能过多;
    • 命令模式 的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难(不过这也是设计模式带来的一个通病,抽象必然会引入额外类型;抽象肯定比紧密难理解);

    使用场景

    • 现实语义中具备 ”命令“ 的操作(如命令菜单,shell命令···);
    • 需要将请求与实现解耦;
    • 需要支持命令的撤销(Undo)操作和恢复(Redo)操作;
    • 需要支持命令组合操作(宏命令);

    模式讲解

    首先看下 命令模式 的通用 UML 类图:

    命令模式

    从 UML 类图中,我们可以看到,命令模式 主要包含四种角色:

    • 接收者角色(Receiver):该类负责具体实施或执行一个请求;
    • 命令角色(Command):定义需要执行的所有命令行为;
    • 具体命令角色(ConcreteCommand):该类内部维护一个 接收者(Receiver),在其execut方法中调用 Receiver 的相关方法;
    • 请求者角色(Invoker):接收客户端的命令,并执行命令;

    :从 命令模式 的 UML 类图中,其实可以很清晰地看出:Command的出现就是作为ReceiverInvoker的中间件,解耦了彼此。而之所以引入Command中间件,我觉得是以下两方面原因:

    • 解耦请求与实现:即解耦了InvokerReceiver,因为在 UML 类图中,Invoker是一个具体的实现,等待接收客户端传入命令(即Invoker与客户端耦合),Invoker处于业务逻辑区域,应当是一个稳定的结构。而Receiver是属于业务功能模块,是经常变动的;如果没有Command,则Invoker紧耦合Receiver,一个稳定的结构依赖了一个不稳定的结构,就会导致整个结构都不稳定了。这也就是Command引入的原因:不仅仅是解耦请求与实现,同时稳定(Invoker)依赖稳定(Command),结构还是稳定的;
    • 扩展性增强:扩展性体现在两个方面:1. Receiver属于底层细节,可以通过更换不同的Receiver达到不同的细节实现;2. Command接口本身就是抽象的,本身就具备扩展性;而且由于命令对象本身就具备抽象,如果结合 装饰器模式,功能扩展简直如鱼得水。

    以下是 命令模式 的通用代码:

    class Client {
        public static void main(String[] args) {
            ICommand cmd = new ConcreteCommand();
            Invoker invoker = new Invoker(cmd);
            invoker.action();
        }
    
        //接收者
        static class Receiver {
            public void action() {
                System.out.println("执行具体操作");
            }
        }
    
        //抽象命令接口
        interface ICommand {
            void execute();
        }
    
        //具体命令
        static class ConcreteCommand implements ICommand {
            // 直接创建接收者,不暴露给客户端
            private Receiver mReceiver = new Receiver();
    
            @Override
            public void execute() {
                this.mReceiver.action();
            }
        }
    
        //请求者
        static class Invoker {
            private ICommand mCmd;
    
            private Invoker(ICommand cmd) {
                this.mCmd = cmd;
            }
    
            public void action() {
                this.mCmd.execute();
            }
        }
    }
    

    :在一个系统中,不同的命令对应不同的请求,也就是说无法把请求抽象化,因此 命令模式 中的Receiver是具体实现;但是如果在某一个模块中,可以对Receiver进行抽象,其实这就变相使用到了 桥接模式Command类具备两个变化的维度:CommandReceiver),这样子的扩展性会更加优秀。

    举个例子

    例子:假如现有我们有一个遥控器,可以控制风扇的风力大小,分为大,中,小,关闭四个程度,请使用程序进行实现。

    分析:上面的例子涉及两个物体:遥控器和风扇,直接的思路就是遥控器紧耦合风扇,然后遥控器内部暴露控制风扇风力等级接口。但是遥控器后续可能还可以对其他电器设备(如空调等)进行控制,因此有必要解耦遥控器和具体电器设备的紧密联系。对遥控器进行操作,相当于发出一个指令(命令),让对应的电器设备进行工作,那么 命令模式 是非常切合这个场景的。

    具体代码如下:

    class Client {
        public static void main(String[] args) {
            RemoteController remote = new RemoteController();
            Fan fan = new Fan();
    
            ICommand cmd = new TurnMinCommand(fan);
            remote.action(cmd);
            
            cmd = new TurnMidCommand(fan);
            remote.action(cmd);
    
            cmd = new TurnMaxCommand(fan);
            remote.action(cmd);
            
            cmd = new TurnOffCommand(fan);
            remote.action(cmd);
        }
    
        //Receiver
        static class Fan {
            public void turnMin() {
                System.out.println("Fan in Min degree");
            }
    
            public void turnMid() {
                System.out.println("Fan in Mid degree");
            }
    
            public void turnMax() {
                System.out.println("Fan in Max degree");
            }
    
            public void turnOff() {
                System.out.println("Fan off");
            }
        }
    
        //Command
        interface ICommand {
            void execute();
        }
    
        //ComcreteCommand
        static class TurnMinCommand implements ICommand {
            private Fan mFan;
    
            public TurnMinCommand(Fan fan) {
                this.mFan = fan;
            }
    
            @Override
            public void execute() {
                this.mFan.turnMin();
            }
        }
    
        //ComcreteCommand
        static class TurnMidCommand implements ICommand {
            private Fan mFan;
    
            public TurnMidCommand(Fan fan) {
                this.mFan = fan;
            }
    
            @Override
            public void execute() {
                this.mFan.turnMid();
            }
        }
    
        //ComcreteCommand
        static class TurnMaxCommand implements ICommand {
            private Fan mFan;
    
            public TurnMaxCommand(Fan fan) {
                this.mFan = fan;
            }
    
            @Override
            public void execute() {
                this.mFan.turnMax();
            }
        }
        //ComcreteCommand
        static class TurnOffCommand implements ICommand {
            private Fan mFan;
    
            public TurnOffCommand(Fan fan) {
                this.mFan = fan;
            }
    
            @Override
            public void execute() {
                this.mFan.turnOff();
            }
        }
    
        //Invoker
        static class RemoteController {
            public void action(ICommand cmd) {
                cmd.execute();
            }
        }
    }
    

    由于遥控器已经与具体电器解耦了,以后如果想扩展新命令,只需增加即可,遥控器结构无需改动。

    相关文章

      网友评论

          本文标题:命令模式

          本文链接:https://www.haomeiwen.com/subject/xorlxqtx.html