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

    命令模式的定义:

      命令模式是对命令的封装,每一个命令都是一个操作:请求方发出请求要求执行一个操作;接收方收到请求,并执行操作。命令模式解耦了请求方

    和接收方,请求方只需请求执行命令,不用关心命令怎么被接收、怎样操作以及是否被执行等。命令模式属于行为型设计模式。

      在软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。但紧耦合关系缺乏扩展性,在某些场合中,需要对行为

    进行记录、撤销或者重做等处理时,只能修改源码。而命令模式通过在请求与实现之间引入一个抽象命令接口。解耦了请求与实现,并且中间件是抽象的,

    它由不同的子类实现,因此具有扩展性。所以,命令模式的本质是解耦命令请求与处理。

    命令模式的应用场景:

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

    命令模式的UML类图:

     

     从上图可以看出,命令模式主要包含4个角色。

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

      从命令模式的UML类图中,可以很清晰地看出,ICommand的出现就是作为Receiver和Invoker的中间件,解耦了彼此。

    使用命令模式重构播放器控制条:

      假如我们开发一个播放器,播放器播放功能、拖动进度条功能、停止播放功能、暂停功能,我们在操作播发器的时候并不知道之间调用播放器

    哪个功能,而是通过一个控制传达去传递指令给播放器内核,具体传达什么指令,会被封装成一个个按钮。那么每个按钮就相当于一条命令的封装。

    用控制条实现了用户发送指令与播放器内核接收指令的解耦。下面来看代码,首先创建播放器内核类:

    public class GPlayer {
        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 GPlayer gplayer;
    
        public PlayAction(GPlayer gplayer) {
            this.gplayer = gplayer;
        }
    
        public void execute() {
            gplayer.play();
        }
    }

    创建暂停指令类:

    public class PauseAction implements IAction {
        private GPlayer gplayer;
    
        public PauseAction(GPlayer gplayer) {
            this.gplayer = gplayer;
        }
    
        public void execute() {
            gplayer.pause();
        }
    }

    创建拖动进度条类:

    public class SpeedAction implements IAction {
        private GPlayer gplayer;
    
        public SpeedAction(GPlayer gplayer) {
            this.gplayer = gplayer;
        }
    
        public void execute() {
            gplayer.speed();
        }
    }

    创建停止播放指令:

    public class StopAction implements IAction {
        private GPlayer gplayer;
    
        public StopAction(GPlayer gplayer) {
            this.gplayer = gplayer;
        }
    
        public void execute() {
            gplayer.stop();
        }
    }

    创建控制条controller类:

    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) {
    
            GPlayer player = new GPlayer();
            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();
        }
    }

    由于控制条已经与播放器内核解耦了,以后如果想扩展新命令,只需要增加命令即可,控制条的结构无须改动。

    命令模式的优点:

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

    命令模式的缺点:

    • 具体命令类可能过多。
    • 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入额外类型结构(引入请求方与抽象命令接口)。
    • 增加了理解上的困难。不过这也是设计模式的通病抽象必然会额外增加类的数量;代码抽离肯定比代码聚合更难理解。
  • 相关阅读:
    Windows下如何检测用户修改了系统时间并且把系统时间改回来
    洛谷 1220 关路灯
    洛谷 2279 [HNOI2003]消防局的设立
    洛谷 1498 南蛮图腾
    bzoj 1036 [ZJOI2008]树的统计Count 树链剖分模板
    codevs 1021 玛丽卡 SPFA
    codevs 1077 多源最短路 flyod
    Vijos P1133 装箱问题 01背包
    codevs 1069 关押罪犯 并查集
    codevs 1073 家族 并查集
  • 原文地址:https://www.cnblogs.com/liu-yi/p/14002139.html
Copyright © 2011-2022 走看看