在本次学习过程中,我们把封装带到一个全新的境界:把方法调用(method invocation)封装起来。没错,通过封装方法调用,我们可以把运算块包装成形。

所以调用此运算的对象不需要关心事情是如何进行的,只要知道如何使用包装成形的方法来完成它就可以。通过封装方法调用,也可以做一些很聪明的事情,例如记录日志,或者重复使用这些封装来实现撤销。让我们开始吧

现在有一个用户A,他们家有很多家电,在装修的时候,他让装修公司把这些家电当成了一个整体,想要通过一个遥控器就能控制家里的电灯、风扇、热水器、音响设备和其他的类似的可控制装置,这样的话就显得很高大上。

所以呢,他让每个厂商投提供了一组Java类,用来控制家电。手上又有一个遥控器,希望我们能够创建一组控制遥控器的API,让每个插槽都能控制一个或一组装置,即通过一个遥控器就能控制所有家电,是不是很酷。

我们先来看下厂商给的类。

这个类确实不少呀,而且接口也各有差异。还有更麻烦的,随着家电数量的增加,这样的类还会越来越多。那么,如何设计一个遥控器API就变得很有挑战性了是吧。

所以,命令模式应运而生了。在我们的设计中,采用“命令模式”,利用命令对象,把请求(例如打开电灯)封装成一个特定对象(例如客厅电灯对象)。所以,如果对每个按钮都存储一个命令对象,那么当按钮被按下的时候,就可以请命令对象做相关的工作。遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。这样,遥控器和电灯对象都解耦了。

可能把遥控器换成餐厅点餐,大家会更容易理解。我在这里简单描述下:首先顾客到了餐厅,根据菜单点了一部分菜,把菜单给服务员;服务员拿到了订单,就提交到柜台上,并向后厨喊了一声“xx号桌xx订单”来了;后厨根据菜单进行配菜,烧菜。全称服务员都不需要知道订单具体内容是什么,只要将桌上的客户点的餐提供给后厨即可,这和我们的遥控器就是一个道理了。

第一个命令对象

实现命令接口

那我们就来创建我们的命令对象吧。首先,我们得让所有的命令对象实现相同的包含一个方法的接口。在餐厅订餐的例子上,就是创建订单,我们在程序的世界里,取名叫做execute()

public interface Command {
    public void execute();
}
实现一个打开电灯的命令

现在,假设想实现一个打开电灯的命令。根据之前看到的厂商提供的类,Light类有两个方法,on和off。

// 这是一个命令,所以需要实现Command接口
public class LightOnCommand implements Command {
    Light light;

// 构造器传入某个电灯,以便让这个命令控制,然后记录在实例变量中
    public LightOnCommand(Light light) {
        this.light = light;
    }

// 这个execute方法调用接收对象的on方法
    public void execute() {
        light.on();
    }

}

使用命令对象

现在,我们让遥控器工作起来,先来点简单的。假设遥控器只有一个按钮和对应的插槽,可以控制一个装置:

public class SimpleRemoteControl {
// 有一个插槽持有命令,而这个命令控制着一个装置
    Command slot;

    public SimpleRemoteControl() {}

// 这个方法用来设置插槽控制的命令
    public void setCommand(Command command) {
        slot = command;
    }

// 当按下按钮时,这个方法就会被调用,使得当前命令衔接插槽,并调用它的execute方法
    public void buttonWasPressed() {
        slot.execute();
    }
}

就这样我们就能实现一个简单的遥控器了。请看我们的测试

public class RemoteControlTest {
    public static void main(String[] args) {
    // 遥控器就是调用者,会传入一个命令对象,可以用来发出请求
        SimpleRemoteControl remote = new SimpleRemoteControl();
    // 现在创建一个电灯对象,此对象也就是请求的接收者
        Light light = new Light();
    // 这里创建一个命令,然后将接收者传给它
        LightOnCommand lightOn = new LightOnCommand(light);

    // 把命令传给调用者
        remote.setCommand(lightOn);
    // 模拟按下按钮
        remote.buttonWasPressed();
    }

}

定义命令模式

经过订餐流程的理解,以及刚才这个小练习,相信你也对命令模式内的类 和对象如何互动理解得很清楚了吧。那我们在这里趁热打铁,定义一下命令模式。

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

仔细想想,我们知道有一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,命令对象将动作和接收者包进对象中。这个对象只暴露出一个execute()方法,当此方法被调用的时候,接收者就会进行这些动作。从外面来看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道如果调用execute()方法,请求的目的就能达到。

让我们来看下命令模式的类图:

好了,通过这个简单的小练习,我们知道如何控制电灯的开关了。在前面厂商给的类中,还有好多方法,比如电风扇、吊灯、电视机、音响等等。控制单个我们已经能搞定了,那控制多个呢?是不是同理呢?还是你有更好的方式呢?小编想请你先动动你的小手,我们下次见分晓。

爱生活,爱学习,爱感悟,爱挨踢

05-13 20:11