《Head First设计模式》读书笔记
命令模式(封装调用)
一,场景介绍
1,需求
设计一个家电自动化遥控器的API,这个遥控器有7个可编程的插槽,每个插槽都有对应的开和关按钮,这个遥控器还有一个整体的撤销按钮。
提供的家电厂商类有很多:Light、Door、TV等
2,思考
- 对于遥控器,它只是知道按下按钮,然后执行对应的ON或者OFF命令,但是他不知道电器执行的具体细节。
- 如果采用判断语句if,else来逐个判断,这样必然违背了设计的基本思想。
- 命令模式刚好符合这个场景,命令模式可以将动作请求(命令)从对象执行对应的操作完全解耦出来,那么学习和理解命令模式就是接下来的重点。
二,介绍命令模式
1,餐厅订餐来理解命令模式
客户(Client)点餐,然后服务员(Invoker)将点餐内容记录下来形成一个订单(Command),然后服务员将订单交给厨房(Receiver),然后厨房做出餐点。首先需要理解的是,作为调度者的服务员,他只需要形成订单(也就是一个家电控制命令),然后将订单送出去,并不需要了解订单的内容是什么,也不需要由哪个厨师来完成,他只是简单的进行一个传递工作。这就是命令模式中一个重要的思想解耦。
2,命令模式中需要学习的重点
在命令模式中,我们不仅要学习命令模式的基本思想结构,我们还要学习,执行基本的单个命令模式,执行多组的命令模式,命令模式的撤销(回退到上一个状态)。最后要知道的就是命令模式的用途。
3,先实行一个基本的单个命令模式
先定义一个命令接口,可以让遥控器设置接口对象的时候都是统一的类型。
public interface Command {
public void execute();
}
定义一个Light类,这个是目标,里面设置的有Light.on()方法。
public class Light {
public Light(){}
public void on(){
System.out.println("Light is on!");
}
}
设置一个开灯的命令来包装一下Light对象
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
设置一个远程命令控制接口,注意思考为什么要设置真么一个控制接口
public class SimpleRemoteControl {
private Command comd;
public SimpleRemoteControl() {
}
public void setCommand(Command command){
this.comd = command;
}
public void buttonWaspressed(){
comd.execute();
}
}
最后一个测试类
public class Test {
public static void main(String[] args) {
SimpleRemoteControl control = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightCommand = new LightOnCommand(light);
control.setCommand(lightCommand);
control.buttonWaspressed();
}
}
三,命令模式的结构图
如下:
Paste_Image.png这个结构图里面多了一个LightOffCommand,其添加方式和上面的简单设计模式实现代码是一样的,而且你还可以添加更多的TVCommand、DoorCommand等,这个可以自己实现。其实到这里我们已经掌握了命令设计模式了,下面只是对命令模式的一些扩展用法介绍,便于在实际开发中灵活扩展。
四,命令的撤回
命令撤回,关于撤回,我们可以联想一下我们日常中Ctrl+Z操作,我们很容易想到将命令放到一个堆栈里面,我们只需要挨个弹出来即可实现。
当然,我们仍然可以按照书中的案例来熟悉一下命令模式。
1,案例介绍
比如吊扇(CeilingFan)他有高速、中速、低速、和关闭状态。沃恩需要记住电扇之前的运行状态,可能是上面四种的任意一个,比如低速,现在我们执行电扇,启动高速,撤回(undo)的时候就变为低速。下面请看代码
2,还是一样,先创建一个Command接口
public interface Command {
public void execute();
public void undo();
}
3,创建一个CeilingFan类
public class CeilingFan {
public static final int HIGH = 3;
public static final int MEDIUM = 2;
public static final int LOW = 1;
public static final int OFF = 0;
int speed;
public CeilingFan() {
speed = OFF;
}
public void high(){
speed = HIGH;
System.out.println("The speed of the ceilingFan is High");
}
public void medium(){
speed = MEDIUM;
System.out.println("The speed of the ceilingFan is medium");
}
public void low(){
speed = LOW;
System.out.println("The speed of the ceilingFan is low");
}
public void off(){
System.out.println("The speed of the ceilingFan is off");
speed = OFF;
}
public int getSpeed(){
return speed;
}
}
4,创建一个CeilingFanHighCommand,当然你可以加上MediumCommand、LowCommand。其中undo就是一个撤回命令。
public class CeilingFanHighCommand implements Command{
private CeilingFan ceilingFan;
private int preSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
preSpeed = ceilingFan.getSpeed();
ceilingFan.high();
}
@Override
public void undo() {
if(preSpeed == CeilingFan.HIGH){
ceilingFan.high();
}else if(preSpeed == CeilingFan.MEDIUM){
ceilingFan.medium();
}else if(preSpeed == CeilingFan.LOW){
ceilingFan.low();
}else if(preSpeed == CeilingFan.OFF){
ceilingFan.off();
}
}
}
5,创建RemoteControl类
public class RemoteControl implements Command{
Command comd;
public RemoteControl() {}
public void setCommand(Command command){
this.comd = command;
}
@Override
public void execute() {
comd.execute();
}
@Override
public void undo() {
comd.undo();
}
}
6,创建测试类
public class Test {
public static void main(String[] args) {
RemoteControl control = new RemoteControl();
CeilingFan ceilingFan = new CeilingFan();
CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
control.setCommand(ceilingFanHighCommand);
control.execute();
control.undo();
}
}
五,运行一组命令
有时候,在特使情景的时候,我们可能需要运行一组命令,比如我们回家,我们想一键完成开门,开灯,然后打开电视等着看剧。或者更多复杂命令的组合要求一键同时执行。下面只实现同时开门,开灯,开电视。
不多说,直接上代码:
1,Command接口统一类型
public interface Command {
public void execute();
}
2,几个实体类
public class Light {
public void on(){
System.out.println("Light is on!");
}
}
public class Door {
public void open(){
System.out.println("The door is openning!");
}
}
public class TV {
public void on(){
System.out.println("the TV is on!");
}
}
3,几个实体类对应的打开命令类
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class DoorOpenCommand implements Command{
private Door door;
public DoorOpenCommand(Door door) {
this.door = door;
}
@Override
public void execute() {
door.open();
}
}
public class TVOnCommand implements Command{
private TV tv;
public TVOnCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.on();
}
}
4,新添加一个AllOnCommand类,注意此处,本来可以硬编码进去,但是为什么没有,体会设计模式的基本原则。
public class AllOnCommand implements Command{
private Command[] command;
public AllOnCommand(Command[] command) {
this.command = command;
}
@Override
public void execute() {
for(Command command:command){
command.execute();
}
}
}
5,测试类
public class Test {
public static void main(String[] args) {
SimpleRemoteControl control = new SimpleRemoteControl();
Light light = new Light();
Door door = new Door();
TV tv = new TV();
LightOnCommand lightCommand = new LightOnCommand(light);
DoorOpenCommand doorOpenCommand =new DoorOpenCommand(door);
TVOnCommand tvCommand = new TVOnCommand(tv);
Command[] commands ={lightCommand,doorOpenCommand,tvCommand};
AllOnCommand allCommand = new AllOnCommand(commands);
control.setCommand(allCommand);
control.buttonWaspressed();
}
}
六,总结
命令模式主要就是要体会封装调用的思想,在实际开发中,消息队列是一个很常用的一个机制,他们不管队列里面是执行什么任务,网络请求呐或者数据运算呐,完全不用管具体业务,只需要执行命令就可以。比如会用在数据库日志中,通过记录命令来达到日志的各种功能,比如数据库恢复。等等。
网友评论