《Head First設(shè)計模式》讀書筆記
命令模式(封裝調(diào)用)
一,場景介紹
1,需求
設(shè)計一個家電自動化遙控器的API,這個遙控器有7個可編程的插槽,每個插槽都有對應(yīng)的開和關(guān)按鈕,這個遙控器還有一個整體的撤銷按鈕。
提供的家電廠商類有很多:Light、Door、TV等
2,思考
- 對于遙控器,它只是知道按下按鈕,然后執(zhí)行對應(yīng)的ON或者OFF命令,但是他不知道電器執(zhí)行的具體細節(jié)。
- 如果采用判斷語句if,else來逐個判斷,這樣必然違背了設(shè)計的基本思想。
- 命令模式剛好符合這個場景,命令模式可以將動作請求(命令)從對象執(zhí)行對應(yīng)的操作完全解耦出來,那么學(xué)習(xí)和理解命令模式就是接下來的重點。
二,介紹命令模式
1,餐廳訂餐來理解命令模式
客戶(Client)點餐,然后服務(wù)員(Invoker)將點餐內(nèi)容記錄下來形成一個訂單(Command),然后服務(wù)員將訂單交給廚房(Receiver),然后廚房做出餐點。首先需要理解的是,作為調(diào)度者的服務(wù)員,他只需要形成訂單(也就是一個家電控制命令),然后將訂單送出去,并不需要了解訂單的內(nèi)容是什么,也不需要由哪個廚師來完成,他只是簡單的進行一個傳遞工作。這就是命令模式中一個重要的思想解耦。
2,命令模式中需要學(xué)習(xí)的重點
在命令模式中,我們不僅要學(xué)習(xí)命令模式的基本思想結(jié)構(gòu),我們還要學(xué)習(xí),執(zhí)行基本的單個命令模式,執(zhí)行多組的命令模式,命令模式的撤銷(回退到上一個狀態(tài))。最后要知道的就是命令模式的用途。
3,先實行一個基本的單個命令模式
先定義一個命令接口,可以讓遙控器設(shè)置接口對象的時候都是統(tǒng)一的類型。
public interface Command {
public void execute();
}
定義一個Light類,這個是目標(biāo),里面設(shè)置的有Light.on()方法。
public class Light {
public Light(){}
public void on(){
System.out.println("Light is on!");
}
}
設(shè)置一個開燈的命令來包裝一下Light對象
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
設(shè)置一個遠程命令控制接口,注意思考為什么要設(shè)置真么一個控制接口
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();
}
}
三,命令模式的結(jié)構(gòu)圖
如下:

這個結(jié)構(gòu)圖里面多了一個LightOffCommand,其添加方式和上面的簡單設(shè)計模式實現(xiàn)代碼是一樣的,而且你還可以添加更多的TVCommand、DoorCommand等,這個可以自己實現(xiàn)。其實到這里我們已經(jīng)掌握了命令設(shè)計模式了,下面只是對命令模式的一些擴展用法介紹,便于在實際開發(fā)中靈活擴展。
四,命令的撤回
命令撤回,關(guān)于撤回,我們可以聯(lián)想一下我們?nèi)粘V蠧trl+Z操作,我們很容易想到將命令放到一個堆棧里面,我們只需要挨個彈出來即可實現(xiàn)。
當(dāng)然,我們?nèi)匀豢梢园凑諘械陌咐齺硎煜ひ幌旅钅J健?/p>
1,案例介紹
比如吊扇(CeilingFan)他有高速、中速、低速、和關(guān)閉狀態(tài)。沃恩需要記住電扇之前的運行狀態(tài),可能是上面四種的任意一個,比如低速,現(xiàn)在我們執(zhí)行電扇,啟動高速,撤回(undo)的時候就變?yōu)榈退?。下面請看代碼
2,還是一樣,先創(chuàng)建一個Command接口
public interface Command {
public void execute();
public void undo();
}
3,創(chuàng)建一個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,創(chuàng)建一個CeilingFanHighCommand,當(dāng)然你可以加上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,創(chuàng)建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,創(chuàng)建測試類
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();
}
}
五,運行一組命令
有時候,在特使情景的時候,我們可能需要運行一組命令,比如我們回家,我們想一鍵完成開門,開燈,然后打開電視等著看劇。或者更多復(fù)雜命令的組合要求一鍵同時執(zhí)行。下面只實現(xiàn)同時開門,開燈,開電視。
不多說,直接上代碼:
1,Command接口統(tǒng)一類型
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,幾個實體類對應(yīng)的打開命令類
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類,注意此處,本來可以硬編碼進去,但是為什么沒有,體會設(shè)計模式的基本原則。
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();
}
}
六,總結(jié)
命令模式主要就是要體會封裝調(diào)用的思想,在實際開發(fā)中,消息隊列是一個很常用的一個機制,他們不管隊列里面是執(zhí)行什么任務(wù),網(wǎng)絡(luò)請求吶或者數(shù)據(jù)運算吶,完全不用管具體業(yè)務(wù),只需要執(zhí)行命令就可以。比如會用在數(shù)據(jù)庫日志中,通過記錄命令來達到日志的各種功能,比如數(shù)據(jù)庫恢復(fù)。等等。