美文网首页程序员想法
Junit中的命令模式

Junit中的命令模式

作者: 碧鬼鸠 | 来源:发表于2019-02-25 13:00 被阅读123次

    Junit中的命令模式

    目录

    • 命令模式介绍
    • Junit中的使用

    命令模式介绍

    • 问题:
      有一个附着多组开关按钮的遥控器,带有可编程插槽,每个都可以指定到一个不同的家电装置;有很多厂商开发的各种家电装置控制类;希望创建一组API,让每个插槽控制一个或一组装置。
    • 考虑:
      不应该让遥控器知道太多厂商类的细节,否则更多的家电加进来,就必须修改代码。
    • 解决方案:
      使用命令模式,将动作的请求者从动作的执行者对象中解耦。
      在该问题中,请求者可以是遥控器,而执行者对象就是厂商类其中之一的实例。
      利用“命令对象”,把请求封装成一个特定对象,每个按钮都存储一个命令对象,当按钮被按下的时候,就可以请命令对象做相关的工作。
      遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。
    • 实例:
      在餐厅,顾客将自己的需求写在订单上,由服务员交给厨师,然后厨师按照订单指令准备餐点。

    命令模式定义

    命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。
    命令模式也支持可撤销的操作。
    命令对象将动作和接收者包进对象中。

    命令模式的类图

    命令模式类图.png

    命令模式实现遥控器的类图

    命令模式实现遥控器的类图.png

    撤销操作的实现

    在Command接口中加入undo()方法,然后每个具体的命令类都实现这个方法:undo方法中调用的操作和execute()方法中的操作相反,比如on的反面是off。
    遥控器类也要做一些修改:加入新的实例变量记录上一个操作,当按下撤销按钮,就调用该实例变量的undo()方法。
    如果撤销到某一个状态需要记录一些变量值,则在execute()方法中加入保存操作前状态数值的语句,然后在undo()的时候恢复。
    如果想要多次撤销,即撤销到很早以前的状态,可以使用一个堆栈记录操作过程的每一个命令。

    宏命令

    通过一个键,控制多种操作。
    在宏命令中,用命令数组存储一大堆命令,当这个宏命令被遥控器执行时,就一次性执行数组里的每个命令。

    命令模式的应用——队列请求

    命令可以将运算块打包(一个接收者和一组动作),然后将它传来传去,就像是对一般的对象一样。
    我们可以利用这样的特性衍生一些应用,例如:日程安排,线程池,工作队列等想象有一个工作队列:你在一端添加命令,然后另一端则是线程。
    线程从队列中取出命令,调用它的execute()方法,等待这个调用完成,然后将此命令对象丢弃,再取出下一个命令……

    命令模式的应用——日志请求

    某些应用需要我们将所有的动作都记录在日志中,并能在系统死机之后,重新调用这些动作恢复到之前的状态。
    通过新增两个方法store()、load(),命令模式就能够支持这一点。
    在Java中,我们可以利用对象的序列化(Serialization)实现这些方法,但是一般认为序列化最好还是只用在对象的持久化上(persistence)。
    怎么做:当我们执行命令的时候,将历史记录储存在磁盘中。一旦系统死机,我们就可以将命令对象重新加载,并成批地依次调用这些对象的execute()方法。

    命令模式构成

    1.客户角色:
    创建一个具体命令对象,并确定其接收者。
    2.命令角色:
    声明一个给所有具体命令类的抽象接口。这是一个抽象角色,通常由一个接口或抽象类实现。
    3.具体命令角色:
    定义一个接收者和行为之间的弱耦合,实现execute方法,负责调用接收者的相应操作。
    4.请求者角色:
    负责调用命令对象执行请求。
    5.接收者角色:
    负责具体实施和执行一个请求。

    代码实例

    一个简单的代码例子,实现命令模式中的各个角色

    接收者

    //接收者
    public class Receiver{
        public void doAction(){
            System.out.println("doAction in Receiver");
         }
    }
    

    抽象的命令角色

    //抽象的命令角色
    public interface Command{
        public void execute();
    }
    

    具体的命令角色

    //具体的命令角色,关联到一个接收者
    public class ConcreteCommand implements Command{
        private Receiver receiver; 
        public ConcreteCommand(Receiver receiver){ 
            this.receiver = receiver;
        }
        
        @Override public void execute(){ 
            //负责调用接收者的相应操作
            receiver.doAction();
        }
    }
    

    命令调用者

    //命令调用者,请求者
    public class Invoker{ 
        private Command command; public Invoker(Command command){            
            this.command = command;
        } 
        public void doInvokerAction(){ 
            // 负责调用命令对象执行请求
            command.execute();
        }
    }
    

    客户角色

    public class Client{ 
    public static void main(String[] args){ 
        // 接收者
        Receiver receiver = new Receiver(); // 命令:具体命令关联接收者
        Command command = new ConcreteCommand(receiver); // 调用者
        Invoker invoker = new Invoker(command); // 调用者调用命令,实际是所关联的接收者执行操作
        invoker.doInvokerAction();
        }
    }
    

    Junit中的体现

    我们编写的TestCase就像是一个命令,通过调用器调用后,JUnit框架中有相应的接收者来执行我们的测试方法。TestCase是真正的测试执行者,每个测试用例都会生成一个TestCase对象,这就是典型的命令者模式。TestCase实现Test接口中定义的方法,测试开发人员只需要关注这些接口方法,而不必关心TestCase以及junit内部的实现细节,run()方法就是Command模式中Execute()方法。

    public void run(TestResult result){  
        result.run(this);  
    }
    

    优点

    • Command模式将实现请求的一方(TestCase)和调用一方(JUnit)进行解耦。
    • Command模式使得新的TestCase很容易加入,无需改变已有的类,只需继承TestCase类即可,这样方便了测试人员。
    • Command模式可以将多个TestCase进行组合成一个复合命令,TestSuite就是复合命令,它使用了Composite模式。
    • Command模式容易把请求的TestCase组合成请求队列,这样使接收请求的一方(JUnit Framework)容易决定是否执行请求,一旦发现测试用例失败或者错误可以立刻停止,进行报告。

    缺点

    由于命令模式只能为一个操作(方法)生成一个对象并给出它的一个"execute(执行)"方法。所以10个操作就要生成10个对象。而且每个类中只能有一个方法,然后由接收者去执行这个方法。

    相关文章

      网友评论

        本文标题:Junit中的命令模式

        本文链接:https://www.haomeiwen.com/subject/hsnwyqtx.html