• 设计模式--命令模式


    简介

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

    ​ 在软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。

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

    命令模式的应用场景

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

    1、现实语义中具备“命令”的操作(如吗,命令菜单,shell命令。。。)

    2、请求调用者和请求的接受者需要解耦,使得调用者和接受者不直接交互;

    3、需要抽象出等待执行的行为,比如撤销(Uodo)操作和恢复(Redo)等操作;

    4、需要支持命令宏(即命令组合操作)

    命令模式的通用UML类图:

    image-20210519235256461

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

    接受者角色(Receiver):该类负责具体实施或执行一个请求;

    命令角色(Command):定义需要执行的所有命令行为;

    具体命令角色(ConcreteCommand):该类内部维护了一个接受者(Receiver),在其execute()方法中调用Receiver的相关方法;

    请求者角色(Invoker):接受客户端的命令,并执行命令。

    ​ 从命令模式的UML类图中,其实可以很清晰的看出:Command的出现就是作为Receiver和Invoker的中间件,解耦了彼此。而之所以引入Command中间件,只要是以下两个方面原因:

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

    扩展性增强:扩展性体现在两方面:

    1、Receiver属于底层细节,可以通过更换不同的Receiver达到不同的细节实现;

    2、Command接口本身就是抽象的,本身就具备扩展性;而且由于命令对象本身就具备抽象,如果结合装饰器模式,功能扩展就变得更加简单。

    命令模式生活中的应用场景

    1、遥控器控制电视

    2、餐厅点菜单

    命令模式在源码中的应用

    1、JDK中的Runnable接口

    Runnable就相当于是命令的抽象,只要是实现了Runnable接口的类都认为是一个线程。

    public interface Runnable {
        public abstract void run();
    }
    

    当调用线程的start()方法之后,就有资格去抢CPU资源,而不需要我们自己编写获得CPU资源的逻辑。而线程抢到CPU资源后,就会执行run()方法的内容,用Runnable接口把用户请求和CPU执行进行了解耦。

    2、junit.framework.Test接口

    public interface Test {
      
        public abstract int countTestCases();
    
        public abstract void run(TestResult result);
    }
    

    Test接口有两个方法,第一个是countTestCases()方法用来统计当前需要执行的测试用例总数。第二个是run()方法就是用来执行具体的测试逻辑,其参数TestResult是用来返回测试结果的。

    实际上我们在编写测试用例的时候,只需要实现Test接口即便认为就是一个测试用例,那么在执行的时候就会自动识别。通常做法都是继承TestCase类。

    public abstract class TestCase extends Assert implements Test {
        
        ...
            
        public void run(TestResult result) {
            result.run(this);
        }
        
        ...
    }
    

    这里TestCase类也实现了Test接口。我们继承TestCase类,相当于也实现了Test接口,自然也会被标记成一个测试用例。

    命令模式的优缺点

    优点:

    1、通过引入中间件(抽象接口),解耦了命令请求与实现;

    2、扩展性良好,可以很容易的新增新命令;

    3、支持组合命令,支持命令队列;

    4、可以在现有命令的基础上,增加额外功能。

    缺点:

    1、具体命令类可能过多;

    2、命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构,解耦请求与实现,引入了额外类结构(引入了请求方与抽象命令接口,增加了理解上的困难(不过设计模式都是这样,抽象必然会引入额外类型;抽象肯定比紧密难理解)。

    代码链接

    命令模式

  • 相关阅读:
    python_xrange和range的异同
    python_学习笔记
    Python IDLE快捷键一览
    Git命令详解
    Git 分支-利用分支进行开发的工作流程
    JavaScript-面向对象
    getWritableDatabase()与getReadableDatabase()的区别:
    「学习笔记」爬山算法与模拟退火
    python基础2
    python基础1
  • 原文地址:https://www.cnblogs.com/snail-gao/p/15068153.html
Copyright © 2020-2023  润新知