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

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

    命令模式的优点:

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

    命令模式的缺点:

    • 具体命令类可能过多。
    • 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入额外类型结构(引入请求方与抽象命令接口)。
    • 增加了理解上的困难。不过这也是设计模式的通病抽象必然会额外增加类的数量;代码抽离肯定比代码聚合更难理解。
  • 相关阅读:
    了解 NoSQL 的必读资料
    关于什么时候用assert(断言)的思考
    这次见到了一些大侠
    NetBeans 时事通讯(刊号 # 87 Jan 12, 2010)
    动态链接库dll,静态链接库lib, 导入库lib
    新女性十得 写得了代码,查得出异常
    记录系统乱谈
    新女性十得 写得了代码,查得出异常
    fullpage.js禁止滚动
    RunningMapReduceExampleTFIDF hadoopclusternet This document describes how to run the TFIDF MapReduce example against ascii books. This project is for those who wants to experiment hadoop as a skunkworks in a small cluster (110 nodes) Google Pro
  • 原文地址:https://www.cnblogs.com/liu-yi/p/14002139.html
Copyright © 2011-2022 走看看