Command 模式是最讓我疑惑的一個模式 , 我在閱讀了很多代碼後 , 才感覺隱約掌握其大概原理 , 我認爲理解設計模式最主要是掌握起原理構造 , 這樣才對自己實際編程有指導作用。 Command 模式實際上不是個很具體 , 規定很多的模式 , 正是這個靈活性 , 讓人有些 confuse 。
Command 定義
不少 Command 模式的代碼都是針對圖形介面的 , 它實際就是功能表命令 , 我們在一個下拉功能表選擇一個命令時 , 然後會執行一些動作。
將這些命令封裝成在一個類別中 , 然後用戶 ( 呼叫者 ) 再對這個類別進行操作 , 這就是 Command 模式 , 換句話說 , 本來用戶 ( 呼叫者 ) 是直接呼叫這些命令的 , 如功能表上打開文檔 ( 呼叫者 ), 就直接指向打開文檔的代碼 , 使用 Command 模式 , 就是在這兩者之間增加一個中間者 , 將這種直接關係拗斷 , 同時兩者之間都隔離 , 基本沒有關係了。
顯然這樣做的好處是符合封裝的特性 , 降低耦合度 ,Command 是將對行爲進行封裝的典型模式 ,Factory 是將創建進行封裝的模式 ,
從 Command 模式 , 我也發現設計模式一個 " 通病 ": 好象喜歡將簡單的問題複雜化 , 喜歡在不同類別中增加第三者 , 當然這樣做有利於代碼的健壯性 可維護性 還有複用性。
如何使用 ?
具體的 Command 模式代碼各式各樣 , 因爲如何封裝命令 , 不同系統 , 有不同的做法。下面事例是將命令封裝在一個 Collection 的 List 中 , 任何物件一旦加入 List 中 , 實際上裝入了一個封閉的黑盒中 , 物件的特性消失了 , 只有取出時 , 才有可能模糊的分辨出 :
典型的 Command 模式需要有一個介面。介面中有一個統一的方法 , 這就是 " 將命令 / 請求封裝爲物件 ":
public interface Command { |
具體不同命令 / 請求代碼是實現介面 Command, 下面有三個具體命令
public class Engineer implements Command { public class Programmer implements Command { public class Politician implements Command { |
按照通常做法 , 我們就可以直接呼叫這三個 Command, 但是使用 Command 模式 , 我們要將他們封裝起來 , 扔到黑盒子 List 裏去 :
public class producer{ public static List produceRequests() { List queue = new ArrayList(); queue.add( new DomesticEngineer() ); queue.add( new Politician() ); queue.add( new Programmer() ); return queue; } } |
這三個命令進入 List 中後 , 已經失去了其外表特徵 , 以後再取出 , 也可能無法分辨出誰是 Engineer 誰是 Programmer 了 , 看下面如何呼叫 Command 模式 :
public class TestCommand { public static void main(String[] args) { List queue = Producer.produceRequests(); for (Iterator it = queue.iterator(); it.hasNext(); ) // 取出 List 中東東 , 其他特徵都不能確定 , 只能保證一個特徵是 100% 正確 , // 他們至少是介面 Command 的 " 兒子 " 。所以強制轉換類別型爲介面 Command ((Command)it.next()).execute(); } |
由此可見 , 呼叫者基本只和介面打交道 , 不合具體實現交互 , 這也體現了一個原則 , 面向介面編程 , 這樣 , 以後增加第四個具體命令時 , 就不必修改呼叫者 TestCommand 中的代碼了。
理解了上面的代碼的核心原理 , 在使用中 , 就應該各人有自己方法了 , 特別是在如何分離呼叫者和具體命令上 , 有很多實現方法 , 上面的代碼是使用 " 從 List 過一遍 " 的做法。這種做法只是爲了演示。
使用 Command 模式的一個好理由還因爲它能實現 Undo 功能。每個具體命令都可以記住它剛剛執行的動作 , 並且在需要時恢復。
Command 模式在介面設計中應用廣泛。 Java 的 Swing 中功能表命令都是使用 Command 模式 , 由於 Java 在介面設計的性能上還有欠缺 , 因此介面設計具體代碼我們就不討論 , 網路上有很多這樣的示例。