自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

命令模式之中介者模式

開發(fā) 前端
最近在跟大家分享設(shè)計模式系列的文章有學(xué)妹問我,命令模式、策略模式、工廠模式。它們分別有啥區(qū)別?看代碼的實現(xiàn)上感覺沒啥區(qū)別呀?

[[412157]]

最近在跟大家分享設(shè)計模式系列的文章有學(xué)妹問我,命令模式、策略模式、工廠模式 它們分別有啥區(qū)別?看代碼的實現(xiàn)上感覺沒啥區(qū)別呀?

我說:文章可能有點長,你忍一下

之前已經(jīng)跟大家分享了策略模式以及工廠模式感興趣的同學(xué)可以再去復(fù)習(xí)一下,今天我們就先重點分析一下命令模式然后再來看看它們的區(qū)別是啥?

命令模式

定義

  • 提供一個統(tǒng)一的方法來封裝命令,通過參數(shù)條件來判斷選擇執(zhí)行什么命令動作。
  • 允許將每一個命令存儲在一個隊列中。

整體結(jié)構(gòu)圖如下:

結(jié)構(gòu)圖中重要角色解釋:

  • Command(命令類):定義命令的抽象封裝類。
  • ConcreteCommand(具體命令類):對Command類進行實現(xiàn),說白了就是具體的命令的實際實現(xiàn)類。
  • Receiver(接收者):執(zhí)行命令關(guān)聯(lián)的操作類。
  • Invoker(調(diào)用者):觸發(fā)命令類,即外部操作事件觸發(fā)執(zhí)行。
  • Client(客戶端):實例化具體命令對象,及接收者的實際類。

整個結(jié)構(gòu)其實看上去還是比較難理解的,但是既然開始在學(xué)設(shè)計模式了,那肯定每種設(shè)計模式都要有了解,來提升自己的知識面

為了加深理解,我還是舉一個好理解的例子:

大家對中國古代君主制度肯定很熟悉?;实劭梢葬槍κ值紫路痰墓屗齻兛梢允杖』蛘甙l(fā)放奏折。那其實這里面我個人感覺就可以體現(xiàn)命令模式。

公公 相當于命令模式的接受者(Receiver),執(zhí)行皇帝的命令,收取早朝奏折(ConcreteCommand) 還是頒布圣旨(ConcreteCommand)

皇帝 相當于命令模式的調(diào)用者(Invoker)

老規(guī)矩,例子說完,看看代碼吧!

  1. // 定義 命令類 
  2. public interface Command { 
  3.     // 執(zhí)行的方法 
  4.     void execute(); 
  5.  
  6. // 定義接收者-公公的角色 
  7. public class Receiver { 
  8.  
  9.     public void Charge(){ 
  10.         System.out.println("收取奏折"); 
  11.     } 
  12.  
  13.     public void Issue(){ 
  14.         System.out.println("頒布圣旨"); 
  15.     } 
  16.  
  17.  
  18. //具體命令類one,收取奏折命令 
  19. public class ConcreteCommandOne implements Command { 
  20.  
  21.     // 接受者,這里可以理解為公公 
  22.     private Receiver receiver; 
  23.  
  24.     public ConcreteCommandOne(Receiver receiver) { 
  25.         this.receiver = receiver; 
  26.     } 
  27.  
  28.     @Override 
  29.     public void execute() { 
  30.         // 收取奏折 
  31.         receiver.Charge(); 
  32.     } 
  33.  
  34. // 具體命令類two,頒布圣旨 
  35. public class ConcreteCommandTwo implements Command { 
  36.  
  37.     // 接受者,這里可以理解為公公 
  38.     private Receiver receiver; 
  39.  
  40.     public ConcreteCommandTwo(Receiver receiver) { 
  41.         this.receiver = receiver; 
  42.     } 
  43.  
  44.     @Override 
  45.     public void execute() { 
  46.         // 頒布圣旨 
  47.         receiver.Issue(); 
  48.     } 
  49.  
  50. // 調(diào)用者,皇帝 
  51. public class Invoker { 
  52.    
  53.     private Command command; 
  54.  
  55.     public Invoker(Command command) { 
  56.         this.command = command; 
  57.     } 
  58.     // 本次需要執(zhí)行的命令 
  59.     public void action() { 
  60.         command.execute(); 
  61.     } 
  62.  
  63.  // 測試demo 
  64.     public static void main(String[] args) { 
  65.         // 實例化一個公公 接收者 
  66.         Receiver receiver =new Receiver(); 
  67.         // 公公 當前能有接收到的幾種命令 
  68.         Command commandOne = new ConcreteCommandOne(receiver); 
  69.         Command commandTwo = new ConcreteCommandTwo(receiver); 
  70.  
  71.         // 皇帝 發(fā)號命令 觸發(fā)執(zhí)行方法 
  72.         Invoker invoker =new Invoker(commandOne); 
  73.         invoker.action(); 
  74.         // result: 收取奏折 
  75.  
  76.         Invoker invokerTwo =new Invoker(commandTwo); 
  77.         invokerTwo.action(); 
  78.         // result:頒布圣旨 
  79.     } 

以上就是簡單的代碼實現(xiàn)了,通過Invoker(皇帝)的選擇可以讓Receiver(公公)確定去執(zhí)行什么命令。這其實就是命令模式的一種簡單體現(xiàn)。

細心的同學(xué)不知道有沒有發(fā)現(xiàn)一個問題,在定義里面

  • 允許將每一個命令存儲在一個隊列中。

我們這里是沒有體現(xiàn)隊列的,其實這個實現(xiàn)也很簡單。在main方法中添加一個隊列就可以了。

  1. public static void main(String[] args) { 
  2.     // 實例化一個公公 接收者 
  3.     Receiver receiver = new Receiver(); 
  4.     // 公公 當前能有接收到的幾種命令 
  5.     Command commandOne = new ConcreteCommandOne(receiver); 
  6.     Command commandTwo = new ConcreteCommandTwo(receiver); 
  7. // 存儲命令 
  8.     Queue<Command> queue = new LinkedList<>(); 
  9.     queue.add(commandOne); 
  10.     queue.add(commandTwo); 
  11. // 批量執(zhí)行 
  12.     for (Command command : queue) { 
  13.         Invoker invoker = new Invoker(command); 
  14.         invoker.action(); 
  15.     } 

這里我想給大家做一個擴展點,這也是我之前看到過一種校驗寫法。

大家在真實的工作中肯定會遇到很多一些接口的校驗,怎么去寫這個校驗邏輯,怎么做到代碼的復(fù)用、抽象等這其實是一個比較難的問題!

還是大致的來看下結(jié)構(gòu)圖吧!!!

 

demo代碼,我也給大家寫出來,需要注意的是我們需要實現(xiàn) ApplicationContextAware 里面的afterPropertiesSet 方法。

  1. // 定義抽象校驗方法 
  2. public abstract class ValidatePlugin { 
  3.     public abstract void validate(); 
  4. // 抽象規(guī)則執(zhí)行器 
  5. public abstract class ValidatePluginExecute { 
  6.     protected abstract List<ValidatePlugin> getValidatePlugins(); 
  7.     public void execute() { 
  8.         final List<ValidatePlugin> validatePlugins = getValidatePlugins(); 
  9.         if (CollectionUtils.isEmpty(validatePlugins)) { 
  10.             return
  11.         } 
  12.         for (ValidatePlugin validatePlugin : validatePlugins) { 
  13.           // 執(zhí)行校驗邏輯,這里大家可以根據(jù)自己的實際業(yè)務(wù)場景改造 
  14.             validatePlugin.validate(); 
  15.         } 
  16.     } 
  17.  
  18. // 具體測試規(guī)則 
  19. @Component("validatePluginOne"
  20. public class ValidatePluginOne extends  ValidatePlugin { 
  21.     @Override 
  22.     public void validate() { 
  23.         System.out.println("validatePluginOne 規(guī)則校驗"); 
  24.     } 
  25.  
  26. // 具體執(zhí)行器,把需要執(zhí)行的規(guī)則添加到 validatePlugins 中 
  27. @Component("testValidatePlugin"
  28. public class TestValidatePlugin extends ValidatePluginExecute implements ApplicationContextAware, InitializingBean { 
  29.  
  30.     protected ApplicationContext applicationContext; 
  31.  
  32.     private List<ValidatePlugin> validatePlugins; 
  33.  
  34.     @Override 
  35.     public void afterPropertiesSet() { 
  36.       // 添加規(guī)則 
  37.         validatePlugins = Lists.newArrayList(); 
  38.         validatePlugins.add((ValidatePlugin) this.applicationContext.getBean("validatePluginOne")); 
  39.  
  40.     } 
  41.  
  42.     @Override 
  43.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
  44.         this.applicationContext = applicationContext; 
  45.     } 
  46.  
  47.     @Override 
  48.     protected List<ValidatePlugin> getValidatePlugins() { 
  49.         return this.validatePlugins; 
  50.     } 
  51.  
  52. // 測試demo 
  53.   public static void main(String[] args) { 
  54.         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 
  55.         TestValidatePlugin testValidatePlugin = (TestValidatePlugin) applicationContext.getBean("testValidatePlugin"); 
  56.         testValidatePlugin.execute(); 
  57.     } 

這個只是一個簡單的測試demo,為了讓大家有一個思考,設(shè)計模式不一定是照搬代碼。更多是開拓自己的視野,提升自己解決問題的能力。

針對不同的一些接口,我們只需要在TestValidatePlugin 中添加具體校驗規(guī)則就可以了,整體的擴展性就變高了,看上去也比較高大上。

所以上面提到的命令模式、策略模式、工廠模式區(qū)別是什么呢?

  • 命令模式:屬于行為型設(shè)計模式,在命令模式中,不同的命令執(zhí)行過程中會產(chǎn)生不同的目的結(jié)果,而且不同的命令是不能替換的。
  • 策略模式 :屬于行為型設(shè)計模式,在策略模式中,重點在于針對每一種策略執(zhí)行,解決根據(jù)運行時狀態(tài)從一組策略中選擇不同策略的問題
  • 工廠模式:屬于創(chuàng)建型設(shè)計模式,在工廠模式中,重點在于封裝對象的創(chuàng)建過程,這里的對象沒有任何業(yè)務(wù)場景的限定,可以是策略,但也可以是其他東西

所以針對設(shè)計模式,其實我理解的還是只說明了一個問題,不同的設(shè)計模式都是為了針對處理不同的場景,不同業(yè)務(wù)場景有不同的寫法。

中介者模式

中介者模式,看這個名字也能理解出來,定一個中間結(jié)構(gòu)來方便管理下游組織。

那么什么是中介模式呢?

  • 在GoF 中的《設(shè)計模式》中解釋為:中介模式定義了一個單獨的(中介)對象,來封裝一組對象之間的交互。將這組對象之間的交互委派給與中介對象交互,來避免對象之間的直接交互。

再來看看這個結(jié)構(gòu)圖吧:

  • Mediator(抽象中介者):用來定義參與者與中介者之間的交互方式
  • ConcreteMediator(具體中介者):實現(xiàn)中介者定義的操作,即就是實現(xiàn)交互方式。
  • Colleague(抽象同事角色):抽象類或者接口,主要用來定義參與者如何進行交互。
  • ConcreteColleague(具有同事角色):很簡單,就是具體的實現(xiàn)Colleague中的方法。

以上結(jié)構(gòu)定義來自設(shè)計模式之美

看這個結(jié)構(gòu)圖理解出來,其實是跟之前為大家寫的一篇觀察者模式有點相同的,感興趣的同學(xué)可以再去復(fù)習(xí)一下。

老規(guī)矩,還是具體舉例代碼實現(xiàn)一下

高鐵系統(tǒng)大家應(yīng)該清楚有一個調(diào)度中心,用來控制每一輛高鐵的進站順序,如果沒有這個調(diào)度中心,當同時有三量高鐵都即將進站時,那他們就需要兩兩相護溝通。

假設(shè)有其中的一輛動車沒有溝通到,那就將發(fā)生不可估量的錯誤,所以就需要通過這個調(diào)度中心來處理這個通信邏輯,同時來管理當前有多少車輛等待進站等。

  1. // 抽象參與者, 也可以使用abstract 寫法 
  2. public interface Colleague { 
  3.    // 溝通消息 
  4.     void message(); 
  5. // 抽象中介者 
  6. public interface Mediator { 
  7.     // 定義處理邏輯 
  8.     void doEvent(Colleague colleague); 
  9.  
  10. // 具體參與者 
  11. @Component 
  12. public class MotorCarOneColleague implements Colleague { 
  13.  
  14.     @Override 
  15.     public void message() { 
  16.         // 模擬處理業(yè)務(wù)邏輯 
  17.         System.out.println("高鐵一號收到消息!?。?quot;); 
  18.     } 
  19. @Component 
  20. public class MotorCarTwoColleague implements Colleague { 
  21.     @Override 
  22.     public void message() { 
  23.         System.out.println("高鐵二號收到消息?。。?quot;); 
  24.     } 
  25. @Component 
  26. public class MotorCarThreeColleague implements Colleague { 
  27.     @Override 
  28.     public void message() { 
  29.         System.out.println("高鐵三號收到消息?。?!"); 
  30.     } 
  31.  
  32. // 具體中介者 
  33. @Component 
  34. public class DispatchCenter implements Mediator { 
  35.   // 管理有哪些參與者 
  36.     @Autowired 
  37.     private List<Colleague> colleagues; 
  38.    
  39.     @Override 
  40.     public void doEvent(Colleague colleague) { 
  41.         for(Colleague colleague1 :colleagues){ 
  42.             if(colleague1==colleague){ 
  43.                 // 如果是本身高鐵信息,可以處理其他的業(yè)務(wù)邏輯 
  44.                 // doSomeThing(); 
  45.                 continue
  46.             } 
  47.           // 通知其他參與 
  48.             colleague1.message(); 
  49.         } 
  50.     } 
  51.  
  52. // 測試demo 
  53. public static void main(String[] args) { 
  54.      // 初始化spring容器 
  55.         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 
  56.      // 獲取中介者,調(diào)度中心 
  57.         DispatchCenter dispatchCenter = (DispatchCenter) applicationContext.getBean("dispatchCenter"); 
  58.  
  59.  
  60.         // 一號高鐵 發(fā)送消息出去 
  61.         MotorCarOneColleague motorCarOneColleague =  (MotorCarOneColleague) applicationContext.getBean("motorCarOneColleague"); 
  62.      // 通過調(diào)度中心溝通信息 
  63.         dispatchCenter.doEvent(motorCarOneColleague); 
  64.         // result:高鐵三號收到消息?。?! 
  65.         //         高鐵二號收到消息!?。?nbsp;
  66.  
  67.  
  68.         // 二號高鐵 發(fā)送消息出去 
  69.         MotorCarTwoColleague  motorCarTwoColleague = (MotorCarTwoColleague)applicationContext.getBean("motorCarTwoColleague"); 
  70.         dispatchCenter.doEvent(motorCarTwoColleague); 
  71.         // result:高鐵一號收到消息?。?! 
  72.         //         高鐵三號收到消息!??! 
  73.  
  74.     } 

中介者模式demo代碼就算完成了,通過這個demo大家應(yīng)該能發(fā)現(xiàn),中介者還是很好理解的。

但是中介者的應(yīng)用場景還是比較少見的,針對一些類依賴嚴重,形成的類似網(wǎng)狀結(jié)構(gòu),改成一個類似與蒲公英一樣結(jié)構(gòu),由中間向外擴散,來達到解耦合的效果。

更多在一個UI界面控件里面比較常見,當然在Java里面java.util.Timer 也可以理解為中介者模式,因為它能控制內(nèi)部線程如何去運行比如多久運行一次等。

上面提到中介者和觀察者模式很像,通過demo代碼大家也能發(fā)現(xiàn)這一點

觀察者模式中觀察者和被觀察者我們基本時固定的,而中介者模式中,觀察者和被觀察者時不固定的,而且中介者可能會最后變成一個龐大的原始類。

總結(jié)

命令模式:雖然不怎么常見,但是我們還是要區(qū)分它與工廠模式以及策略模式的區(qū)別是啥,應(yīng)用場景是啥,能給我們帶來什么思考。

比如我最后的那個例子,命令模式可以實現(xiàn)命令的存儲,本質(zhì)是將命令維護在一個隊列中,那么在我們的業(yè)務(wù)代碼中 我們?yōu)槭裁床荒芤餐ㄟ^一個數(shù)組來維護一些接口校驗依賴,里面存放需要校驗的bean實例。來提高代碼的復(fù)用性以及擴展性。

中介模式:整體來說這個更加不怎么應(yīng)用,雖然能起到對象的解耦合,但是也有副作用,而且在我們的真實業(yè)務(wù)場景中也很少會遇到這樣的場景,了解一下實現(xiàn)原理即可,至于與觀察者的區(qū)別,上面也有講到,更多我們可能是已經(jīng)在使用一些中間件消息隊列去處理了。

我是敖丙,你知道的越多,你不知道的越多,我們下期見!

 

責(zé)任編輯:姜華 來源: 三太子敖丙
相關(guān)推薦

2024-06-05 09:41:41

2020-11-05 09:38:07

中介者模式

2023-05-26 08:41:23

模式Go設(shè)計模式

2023-10-07 00:17:06

AirDrop中介者模式

2023-09-27 08:43:17

MediatR中介者模式庫

2020-10-20 13:33:00

建造者模式

2021-10-26 00:21:19

設(shè)計模式建造者

2023-05-04 08:47:31

命令模式抽象接口

2020-11-03 13:05:18

命令模式

2024-05-13 10:45:25

中介模式面向?qū)ο?/a>數(shù)量

2020-12-01 07:16:05

重學(xué)設(shè)計模式

2021-01-21 05:34:14

設(shè)計模式建造者

2020-10-26 08:45:39

觀察者模式

2012-07-10 02:01:53

設(shè)計模式命令模式

2010-07-19 17:44:26

Telnet命令

2012-05-16 17:15:04

Java設(shè)計模式

2021-07-08 11:28:43

觀察者模式設(shè)計

2013-11-26 17:09:57

Android設(shè)計模式

2010-04-13 08:54:28

PHP設(shè)計模式命令模式

2024-06-04 13:11:52

Python行為設(shè)計模式開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號