假设现有一个新需求要求我们统计方法的执行时间.假设已有一个Command接口,其主要代码如下所示:
public abstract void execute();
还有一个Command Timer类:
package com.oozinoz.utility;
import com.oozinoz.robotInterpreter.Command;
public class CommandTimer
{
public static long Time(Command command){
long t1 = System.currentTimeMillis();
command.execute();
long t2 = System.currentTimeMillis();
return t2-t1;
}
}
可以使用JUnit测试来测试time()方法,代码类似于下面的语句.请注意,这不是准确的测试;如果计时器是"jittery",程序就会失败.
突破题:请填写完整上面用于统计doze命令执行时间的赋值语句.
答:testSleep()方法将doze命令传递给CommandUtil类的time()工具方法.如上面注释部分所示.
3.Command模式钩子:
前面介绍了Aster火药球填压机,它是一种智能机器,其代码应用了Template Method模式.在该代码中,我们重写了其中的markMoldIncomplete()方法;当我们关闭填压机的时候,如果正在处理模具,那么该方法则会将该模具标识为未完成的模具.
AsterStarPress类是一个抽象类,在它的子类中,我们必须重写markMoldIncomplete()方法,AsterStarPress类的shutdown()方法就是依赖这个方法来确保域对象知道哪些模具是未完成的.
public void shutdown(){
if(inProcess()){
stopProcessing();
markMoldIncomplete(currentMoldID);
}
usherInputMolds();
dischargePaster();
flush();
}
我们会将有些类移到火药球填压机的板载计算机上,因而由AsterStarPress类派生出子类可能会不方便.假设我们要求AsterStarPress类的开发人员应用Command模式来提供一个钩子.图1描述了一个Hook命令,AsterStarPress类可以使用这个命令,这样我们就可以将焰火填压机的代码在运行时参数化.
我们可以在类中提供一个钩子,从而允许用户插入定制的代码.
所谓的钩子就是在某段代码的特定位置调用用户提供的命令
最初,AsterStarPress类将它的钩子设置为NullHook对象.这个NullHook对象的execute()方法并不执行任何操作.我们可以提供自己实现的Hook对象,以便shutDown()方法调用.在本例中,shutDown()方法将在适当的时刻以AsterStarPress对象为参数调用钩子的特定方法,具体代码如下:
public void shutDown(){
if(inProcess()){
stopProcessing();
moldIncompleteHook.execute(this); //突破题要求完成的语句
}
usherInputMolds();
dischargePaste();
flush();
}
请注意,这个方法并没有干扰检测moldIncompleteHook是否是null,因为始终被设置为一个真实的Hook对象(最初它被设置为不做任何事情的NullHook对象,用户可以配置不同的钩子).
它的用法如下:
package app.templateMethod;
import com.oozinoz.businessCore.*;
import aster2.*;
public class ShowHook
{
public static void main(String[] args)
{
AsterStarPress p = new AsterStarPress();
Hook h = new Hook(){
public void execute(AsterStarPress p){
MaterialManager m = MaterialManager.getManager();
m.setMoldIncomplete(p.getCurrentMoldID());
}
};
p.setMoldIncompleteHook(h);
}
}
突破题:请完成shutDown()方法的代码.
本例演示了另外一个模式,NULL Object模式,这个模式没有<<设计模式>>一书中的模式那么著名.通过引入默认的对象,该模式可以避免空指针检查.
Command模式提供除Template Method模式之外的另一种设计钩子的方法.此外,Command模式在设计意图或者设计结构上非常类似于其他几种设计模式.
4.Command模式与其他模式的关系:
Command模式类似于Interpreter模式.这两个模式将在下一章进一步进行比较.Command模式也类似于我们前面介绍的一个模式,客户知道应该在何时创建对象,但并不知道该实例化哪个类.?
突破题:请问在哪个模式中,客户知道应该在何时创建对象,但并不知道该实例化哪个类?
答:在Factory Method模式中,用户知道何时创建一个新的对象,但并不知道该创建一个什么类型的对象.Factory Method模式将创建对象的工作移入另一个方法中,从而使得用户无需知道该对哪个类进行实例化.这一原则同样适用于Abstract Factory模式.
除了类似于其他模式之外,Command模式通常和其他模式合作使用.比如,你也许在MVC设计中同时使用Command模式和Mediator模式.Mementos模式里就给出这样一个例子.Visualization类处理GUI控制逻辑,但是中介者负责任何与模型相关的逻辑处理.比如,Visualization类使用如下代码来滞后实例化Undo按钮:
protected JButton undoButton()
{
if(undoButton == null){
undoButton = ui.createButtonCancel();
undoButton.setText("undo");
undoButton.setEnabled(false):
undoButton.addActionListener(mediator.undoAction());
}
return undoButton;
}
上述代码应用了Command模式,把undo()方法打包放在ActionListener类的一个实例中.这部分代码也使用Mediator模式,让中心对象处理与底层对象模型相关的事件.为保证undo()方法正确工作,中介者代码必须恢复模拟工厂的以前版本,保证应用经常与Command模式一起使用的其他模式.
突破题:请问哪种模式可以实现对象状态的存储和恢复?
答:Memento模式的意图在于为对象状态提供存储和恢复的机制.通常,每当执行一个命令的时候,就向栈中压入一个新的备忘录;当用户需要撤销前面所执行的命令的时候,可以从栈中弹出一个备忘录,并将它应用于当前的程序.
5.小结:
Command模式将请求封装在对象中,这样就可以像管理对象一样管理调用,当时机和环境适合时进行调用.菜单是Command模式的一个典型实例,它能够充分体现该模式的价值.在菜单的设计中,菜单项知道应该在何时执行操作,但是并不知道将会调用哪个方法.我们通过应用Command模式可以将菜单项对应的方法调用作为参数传给菜单.
Command模式的另外一个用法是允许在服务的上下文中执行客户代码.服务经常在客户代码调用前和调用后运行.最后,除了控制方法执行的时机或者上下文之外,Command模式还可以提供钩子,允许可选的客户代码作为算法的一部分执行.
Command模式把请求封装在一个对象中,所以可以像操作其他对象一样来管理它.可能是由于Command模式所应用的都是一些非常基础的思想,因而它与其他许多设计模式都存在着有趣的联系.比如,Command模式可以作为Template Method模式的一种替代模式,此外,该模式还可以与Mediator模式和Memento模式共同使用.