命令模式(Command Pattern),給大家的第一感覺,就是給程式傳送命令,比如:啟動、暫停,然後程式根據接收到的命令直接執行就行。
這樣的理解相對來說比較狹義,來看下命令模式官方的定義:
將一個請求封裝為一個物件,使發出請求的責任和執行請求的責任分割開。這樣兩者之間透過命令物件進行溝通,這樣方便將命令物件進行儲存、傳遞、呼叫、增加與管理。
一個太狹義,一個又太晦澀。(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
我對命令模式的理解是這樣的:我們將請求引數,以及請求的執行邏輯、依賴物件等,封裝在一個物件中,將這個物件推送到執行的引擎中,由執行引擎來驅動執行。我們透過命令模式可以更好的封裝邏輯、管理邏輯。
它是物件導向的23種設計模式中的一種,屬於行為模式的範圍。
我們來看這樣的一個例子:
工作介面類
1 package com.example.demo.learn.pattern.behavior.command; 2 3 /** 4 * @discription 5 */ 6 public interface IWorkCommand { 7 8 String getWorkName(); 9 10 void execute(); 11 }
司機工作類
1 package com.example.demo.learn.pattern.behavior.command; 2 3 import lombok.AllArgsConstructor; 4 import lombok.extern.slf4j.Slf4j; 5 6 /** 7 * @discription 8 */ 9 @AllArgsConstructor 10 @Slf4j 11 public class DriverWork implements IWorkCommand { 12 13 private String name; 14 15 @Override 16 public String getWorkName() { 17 return "Driver: " + name; 18 } 19 20 @Override 21 public void execute() { 22 log.warn("start invoke {} work", getWorkName()); 23 log.warn("運送貨物到指定目的地"); 24 log.warn("打掃汽車衛生"); 25 log.warn("將汽車送回停車地點"); 26 } 27 }
程式設計師工作類
1 package com.example.demo.learn.pattern.behavior.command; 2 3 import lombok.AllArgsConstructor; 4 import lombok.extern.slf4j.Slf4j; 5 6 /** 7 * @discription 8 */ 9 10 @AllArgsConstructor 11 @Slf4j 12 public class ProgrammerWork implements IWorkCommand{ 13 14 private String name; 15 @Override 16 public String getWorkName() { 17 return "programmer: "+name; 18 } 19 20 @Override 21 public void execute() { 22 log.warn("start invoke {} work", getWorkName()); 23 log.warn("修復昨天遺留的問題."); 24 log.warn("完成今天的開發工作."); 25 log.warn("最佳化系統效能和穩定性."); 26 } 27 }
執行中心
1 package com.example.demo.learn.pattern.behavior.command; 2 3 /** 4 * @discription 5 */ 6 public class InvokeCenter { 7 public void invokeWork(IWorkCommand workCommand) { 8 9 workCommand.execute(); 10 } 11 }
主類
1 package com.example.demo.learn.pattern.behavior.command; 2 3 /** 4 * @discription 5 */ 6 public class PatternMain { 7 public static void main(String[] args) { 8 IWorkCommand programmerWork = new ProgrammerWork("小p"); 9 IWorkCommand driverWork = new DriverWork("小d"); 10 InvokeCenter invokeCenter = new InvokeCenter(); 11 invokeCenter.invokeWork(driverWork); 12 invokeCenter.invokeWork(programmerWork); 13 } 14 }
輸出結果是這樣的:
Connected to the target VM, address: '127.0.0.1:52437', transport: 'socket' 15:57:07.856 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - start invoke Driver: 小d work 15:57:07.866 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - 運送貨物到指定目的地 15:57:07.866 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - 打掃汽車衛生 15:57:07.866 [main] WARN com.example.demo.learn.pattern.behavior.command.DriverWork - 將汽車送回停車地點 15:57:07.867 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - start invoke programmer: 小p work 15:57:07.867 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - 修復昨天遺留的問題. 15:57:07.868 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - 完成今天的開發工作. 15:57:07.868 [main] WARN com.example.demo.learn.pattern.behavior.command.ProgrammerWork - 最佳化系統效能和穩定性. Disconnected from the target VM, address: '127.0.0.1:52437', transport: 'socket' Process finished with exit code 0
在這個例子中,我們並不直接執行各種具體的工作,而是將他們都封裝到一段方法中,由執行引擎統一的來執行。
這段邏輯是不是和透過例項化Thread 的方式,進行多執行緒操作的邏輯很像?(請參考這篇文章)
沒錯,在多執行緒的執行中,就應用到了命令模式。(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
我們結合命令模式,可以發現這種結構大概有這3個角色:
1、一個抽象的命令介面(抽象類),在這裡是 ,他用來約定我們要執行的方法放在哪裡,怎麼執行。我們一般稱之為抽象命令類 Command
2、實現了抽象命令的實現類,在這裡是,我們一般透過編寫這個類,來實現我們想要的邏輯。我們一般稱之為具體命令類 Concrete Command
3、呼叫者,在這裡是,我們一般透過呼叫者來儲存和執行命令,一般也稱之為 請求者/呼叫者 invoker
除此之外還有一個角色
實現者(Receiver),這個角色,在我們的示例中隱藏的有點深,是log物件,也就是命令物件中,真正執行邏輯操作的物件。
注意呼叫者invoker 是不直接持有實現者的,兩者是沒有耦合關係的,是透過持有命令物件,間接的持有了呼叫者,間接的驅動了呼叫者。這樣做既可以讓呼叫者不關心具體的業務(譬如說執行緒池從來不直接持有執行物件的引用,而只持有對應的執行方法 (run()),由執行方法來組織邏輯和解耦)。
類圖大概是這樣子:
有些人問,我直接依賴呼叫者,然後呼叫呼叫者的某些方法,來實現我需要的邏輯是否可以,
答案是可以,但是如果第三方想要執行你的這段邏輯、或者你需要將這段邏輯交給第三方去在特定的時機處理執行,你會怎麼做呢?
這時候你就需要將你實現的這段邏輯封裝到一個物件中,交給其他人,這時候最終又變成了命令模式的體現。
命令模式除了進行解耦,還有一個好處就是可以編排和管理業務邏輯(命令)。
舉個例子:有時候我們要做的一個業務包含幾件相關的事,事情之間沒有先後順序,
比如我們去超市買一瓶可樂,需要做:
1、拿可樂
2、支付
此時我們就可以將每件事各自封裝成一個命令,將整個業務包含的命令,打包丟給執行引擎,這樣是不是就很好處理業務了。
A業務要做:a,b,c 三個命令
B業務要做:a,c,d,e 四個命令
我們只要定義每個命令,然後封裝好每塊業務需要做的幾件事(命令),這樣面相物件的設計感覺一下子就出來了。