天天看點

指令模式

當學了這個指令模式後,又一次體會到程式設計的藝術,明明一個看似很簡單的事,卻要用“複雜”的方法來實作,就像在之前我多次說到的,其實并不是“複雜”,并不是“難”,而是自己基本功太弱,這些看似“複雜”的設計并不是故弄玄機賣弄,仔細學習過後才發現之精妙。比如今天要說的指令模式,書中舉的例子非常典型。去路邊攤買燒烤,我是客戶,老闆是烤燒烤的,這個可以簡單畫一下UML類結構圖。

指令模式

這是我們一貫的思維,烤羊肉、烤雞翅作為烤燒烤者的兩個類,我作為買燒烤者(用戶端)直接通過烤燒烤者類調用“烤羊肉”、“烤雞翅”方法,這實際上是非常緊的耦合,客戶多了,燒烤攤就會亂。是以我們要想如何來将代碼解耦呢?同樣,還是這個例子,有路邊攤,但是也有燒烤店,在燒烤店裡服務員給我們一個菜單,服務員将我們點好的菜單再轉交給烤串師傅,在這期間我們不必關心是誰烤的,我們也不會一直盯着他烤串,我們吃多吃少吃鹹吃淡這一切在菜單裡都有記錄,我們坐等上烤串就可以了。通過燒烤店我們就可以引申出一個設計模式——指令模式。

我們思考,“燒烤店模式”在中間多了一個服務員,通過服務員發送“指令”給師傅來給我們烤串,具體的烤串過程,我不關心服務員也不關心。

指令模式
一個基本的雛形好像就出來了,我們再繼續,我們把烤羊肉、烤雞翅當做一個一個指令,就可以将指令抽象出來了。
指令模式
這好像差不多了,不過我們的烤串師傅呢?我們說了烤串師傅才是具體負責烤羊肉、烤雞翅的,是以就是烤羊肉、烤雞翅依賴于烤串師傅。
指令模式

通過上面的UML類結構圖,我們用代碼來實作燒烤店的烤串場景。

首先我們把指令接口改為抽象類,因為我們要傳遞一個具體的烤串者進去。

1 package day_8_command;
 2 
 3 /**
 4  * 指令抽象類,通過構造函數可提供具體的烤串師傅
 5  * @author 餘林豐
 6  *
 7  * 2016年10月8日
 8  */
 9 public abstract class AbstractCommand {
10     protected Barbecurer barbecurer;
11     
12     public AbstractCommand(Barbecurer barbecurer){
13         this.barbecurer = barbecurer;
14     }
15     
16     public abstract void excuteCommand();
17 }      

實作一個具體的指令類即可。

1 package day_8_command;
 2 
 3 /**
 4  * 具體指令
 5  * @author 餘林豐
 6  *
 7  * 2016年10月9日
 8  */
 9 public class Command1 extends AbstractCommand {
10 
11     /**
12      * @param barbecurer
13      */
14     public Command1(Barbecurer barbecurer) {
15         super(barbecurer);
16     }
17 
18     /* (non-Javadoc)
19      * @see day_8_command.AbstractCommand#excuteCommand()
20      */
21     @Override
22     public void excuteCommand() {
23         System.out.println("開始烤羊肉串");
24     }
25 
26     @Override
27     public String toString() {
28         return "烤羊肉串";
29     }
30 
31 }      

接着是服務員負責從客戶這裡取回菜單向師傅喊指令烤串。

1 package day_8_command;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * 服務員
 8  * @author 餘林豐
 9  *
10  * 2016年10月8日
11  */
12 public class Waiter {
13     private List<AbstractCommand> order = new ArrayList<AbstractCommand>();
14     
15     /**
16      * 設定訂單
17      * @param command 具體指令,也就是具體烤什麼
18      */
19     public void setOrder(AbstractCommand command){
20         order.add(command);
21         System.out.println("增加訂單:" + command.toString());
22     }
23     
24     /**
25      * 取消訂單
26      * @param command
27      */
28     public void cancelOrder(AbstractCommand command){
29         order.add(command);
30         System.out.println("取消訂單:" + command.toString());
31     }
32     
33     /**
34      * 通知全部執行
35      */
36     public void notifyX(){
37         for (AbstractCommand cmd : order){
38             cmd.excuteCommand();
39         }
40     }
41 }      

具體的烤串師傅,在這裡實際并沒有用到它多少。

1 package day_8_command;
 2 
 3 /**
 4  * 烤串師傅
 5  * @author 餘林豐
 6  *
 7  * 2016年10月8日
 8  */
 9 public class Barbecurer {
10     public void action(){
11         System.out.println("開始執行");
12     }
13 }      
1 package day_8_command;
 2 
 3 /**
 4  * 用戶端測試類
 5  * @author 餘林豐
 6  *
 7  * 2016年10月9日
 8  */
 9 public class Main {
10 
11     /**
12      * @param args
13      */
14     public static void main(String[] args) {
15         Barbecurer boy = new Barbecurer();
16         Command1 command1 = new Command1(boy);
17         Waiter waiter = new Waiter();
18         waiter.setOrder(command1);
19         waiter.notifyX();
20     }
21 
22 }      

看下執行結果。

指令模式

一定要從UML類結構圖開始到編碼自己手動過一遍,這僅僅是學習,離實際應用還有很長的了解時間,就比如說在這裡我們并沒有利用多少“烤串者”這個類,但這不代表在指令模式下就不需要它。最後來總結下指令模式的優點:它能把請求一個操作的對象與知道怎麼執行一個操作的對象分隔開。最後說個題外話,對于靈活開發原則,當我們并不清楚一個系統是否需要指令模式時,我們不用絞盡腦汁去實作,在以後的系統版本中如果需要用到指令模式,再通過代碼重構為指令模式。這就涉及靈活開發和重構了,再将設計模式學習完過後接着就是聊聊重構。

不積跬步,無以至千裡;不積小流,無以成江海。

上一篇: 中介者模式
下一篇: 備忘錄模式