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();
        }
    }

    优点:

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

    缺点:

    • 具体命令类可能过多;
    • 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难(不过这也是设计模式带来的一个通病,抽象必然会引入额外类型;抽象肯定比紧密难理解)。
  • 相关阅读:
    网站架构探索(3)负载均衡的方式
    架构师之路(6)OOD的开闭原则
    也谈IT人员流失问题 王泽宾
    技术体系的选择之Java篇
    网站架构探索(2)CDN基本常识
    设计模式之单例模式
    网站架构探索(1)序言 王泽宾
    架构师之路(39)IoC框架
    发展之道:简单与专注
    修me30打印机
  • 原文地址:https://www.cnblogs.com/wuzhenzhao/p/12557341.html
Copyright © 2011-2022 走看看