當學了這個指令模式後,又一次體會到程式設計的藝術,明明一個看似很簡單的事,卻要用“複雜”的方法來實作,就像在之前我多次說到的,其實并不是“複雜”,并不是“難”,而是自己基本功太弱,這些看似“複雜”的設計并不是故弄玄機賣弄,仔細學習過後才發現之精妙。比如今天要說的指令模式,書中舉的例子非常典型。去路邊攤買燒烤,我是客戶,老闆是烤燒烤的,這個可以簡單畫一下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類結構圖開始到編碼自己手動過一遍,這僅僅是學習,離實際應用還有很長的了解時間,就比如說在這裡我們并沒有利用多少“烤串者”這個類,但這不代表在指令模式下就不需要它。最後來總結下指令模式的優點:它能把請求一個操作的對象與知道怎麼執行一個操作的對象分隔開。最後說個題外話,對于靈活開發原則,當我們并不清楚一個系統是否需要指令模式時,我們不用絞盡腦汁去實作,在以後的系統版本中如果需要用到指令模式,再通過代碼重構為指令模式。這就涉及靈活開發和重構了,再将設計模式學習完過後接着就是聊聊重構。
不積跬步,無以至千裡;不積小流,無以成江海。