zoukankan      html  css  js  c++  java
  • 设计模式之命令模式

    命令模式:

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

      将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能,在软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。但紧耦合关系缺乏扩展性,在某些场合中,当需要为行为进行记录,撤销或重做等处理时,只能修改源码。而命令模式通过为请求与实现间引入一个抽象命令接口,解耦了请求与实现,并且中间件是抽象的,它可以有不同的子类实现,因此其具备扩展性。所以,命令模式的本质是解耦命令请求与处理。

    命令模式的应用场景:

      当系统的某项操作具备命令语义时,且命令实现不稳定(变化),那么可以通过命令模式解收方与抽象命令接口呈现弱耦合(内部方法无需一致),具备良好的扩展性。命令模式适用于以下应用场景: 

    • 现实语义中具备“命令”的操作(如命令菜单, shell命令…) ;
    • 请求调用者和请求的接收者需要解耦,使得调用者和接收者不直接交互;
    • 需要抽象出等待执行的行为, 比如撤销(Undo) 操作和恢复(Redo) 等操作;
    • 需要支持命令宏(即命令组合操作)。

    命令模式主要包含四种角色:

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

      其实可以看出:Command的出现就是作为Receiver 和Invoker的中间件, 解耦了彼此。而之所以引入Command中间件, 我觉得是以下两方面原因:  

    解耦请求与实现:即解耦了Invoker和Receiver, Invoker是一个具体的实现, 等待接收客户端传入命令(即Invoker与客户端耦合) , Invoker处于业务逻辑区域,应当是一个稳定的结构。而Receiver是属于业务功能模块是经常变动的如果没有Command,则Invoker紧耦合Receiver, 一个稳定的结构依赖了一个不稳定的结构, 就会导致整个结构都不稳定了。这也就是Command引入的原因:不仅仅是解耦请求与实现, 同时稳定(Invoker)依赖稳定(Command) , 结构还是稳定的。扩展性增强:扩展性体现在两个方面:

    • Receiver属于底层细节, 可以通过更换不同的Receiver达到不同的细节实现;
    • Command接口本身就是抽象的, 本身就具备扩展性; 而且由于命令对象本身就具备抽象,如果结合装饰器模式,功能扩展简直如鱼得水.

      举个生活中的例子,相信80后的小伙伴应该都经历过普及黑白电视机的那个年代。黑白电视机要换台那简直不容易,需要人跑上前去用力掰动电视机上那个切换频道的旋钮,一顿“啪啪啪”折腾下来才能完成一次换台。如今时代好了,我们只需躺沙发上按一下遥控器就完成了换台。这就是用到了命令模式,将换台命令和换台处理进行了分离。另外,就是餐厅的点菜单,一般是后厨先把所有的原材料组合配置好了,客户用餐前只需要点菜即可,将需求和处理进行了解耦。

    命令模式在业务场景中的应用:

      假如我们自己开发一个播放器,播放器有播放功能、有拖动进度条功能、停止播放功能、暂停功能,我们自己去操作播放器的时候并不是直接调用播放器的方法,而是通过一个控制条去传达指令给播放器内核,那么具体传达什么指令,会被封装为一个一个的按钮。那么每个按钮就相当于是对一条命令的封装。用控制条实现了用户发送指令与播放器内核接收指令的解耦。下面来看代码, 首先创建播放器内核 Player类:

    public class Player {
    
        public void play() {
            System.out.println("正常播放");
        }
    
        public void speed() {
            System.out.println("拖动进度条");
        }
    
        public void stop() {
            System.out.println("停止播放");
        }
    
        public void pause() {
            System.out.println("暂停播放");
        }
    
    }

      创建命令执行接口:

    public interface IAction {
        void execute();
    }

      然后创建操作指令类:

    //播放
    public class PlayAction implements IAction {
        private Player player;
    
        public PlayAction(Player player) {
            this.player = player;
        }
    
        public void execute() {
            player.play();
        }
    }
    //暂停
    public class PauseAction implements IAction {
        private Player player;
    
        public PauseAction(Player player) {
            this.player = player;
        }
    
        public void execute() {
            player.pause();
        }
    }
    //拖动进度条
    public class SpeedAction implements IAction {
        private Player player;
    
        public SpeedAction(Player player) {
            this.player = player;
        }
    
        public void execute() {
            player.speed();
        }
    }
    //停止
    public class StopAction implements IAction {
        private Player player;
    
        public StopAction(Player player) {
            this.player = player;
        }
    
        public void execute() {
            player.stop();
        }
    }

      最后创建控制类:

    public class Controller {
        private List<IAction> actions = new ArrayList<IAction>();
    
        public void addAction(IAction action){
            actions.add(action);
        }
    
        public void execute(IAction action){
            action.execute();
        }
    
        public void executes(){
            for (IAction action:actions) {
                action.execute();
            }
            actions.clear();
        }
    
    }

      测试:

    public class Test {
        public static void main(String[] args) {
    
            Player player = new Player(); 
            //单条指令
            Controller controller = new Controller();
            controller.execute(new PlayAction(player));
            //批量指令
            controller.addAction(new PauseAction(player));
            controller.addAction(new PlayAction(player));
            controller.addAction(new StopAction(player));
            controller.addAction(new SpeedAction(player));
            controller.executes();
        }
    }

    优点:

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

    缺点:

    • 具体命令类可能过多;
    • 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难(不过这也是设计模式带来的一个通病,抽象必然会引入额外类型;抽象肯定比紧密难理解)。
  • 相关阅读:
    topcoder srm 445 div1
    topcoder srm 440 div1
    topcoder srm 435 div1
    topcoder srm 430 div1
    topcoder srm 400 div1
    topcoder srm 380 div1
    topcoder srm 370 div1
    topcoder srm 425 div1
    WKWebView强大的新特性
    Runtime那些事
  • 原文地址:https://www.cnblogs.com/wuzhenzhao/p/12557341.html
Copyright © 2011-2022 走看看