假設你的公司時常會收到不同的電子郵件如垃圾信,顧客的抱怨信,顧客的感謝信等等,而公司希望對這些不同類型的信都先用制式的罐頭信回應 (就是不同的信件有不同的處理方式)。當你要寫程式處理公司的電子郵件時,直接寫在一個 class 是最簡單的作法。但你勢必會用到 if/else 或 switch 等來判斷不同的信件,而這樣做有一些缺點,例如程式複雜性增加,不夠彈性,擴充困難等等。當你希望多個物件 (處理信的方式) 都有機會能處理相同請求時 (新寄來的電子郵件) 時,可以考慮責任鏈模式。先介紹此模式的正式定義及類別圖:
從程式碼可以看到,我們在建構 MailHandler 物件時,同時也指定要接在之後處理的物件為何,當然這個順序是可以自定義的。通常都是由小範圍到大範圍,特殊情形至一般情形來組織責任鏈,如上例把 GeneralMailHandler 放在最後面。
另外可能有人會覺得責任鏈物件生成的方式很像裝飾者模式,但這兩種模式意義卻是完全不同的喔。裝飾者模式的主要用途在增加元件的行為,而責任鏈模式是在組織處理請求的元件,範例中生成元件的部份也同時在指定元件順序,並沒有增加元件的行為。
責任鏈模式可以將請求的發出者和接受者之間予以鬆綁。請求者不需要知道實際接受者是誰,也不用知道請求是如何被處理,各個接受者之間也是彼此獨立且鬆綁的。責任鏈模式也可以動態修改責任鏈,如新增或刪除處理請求的物件。因為上述的特性,因此很常用來使用在視窗程式中,處理像是滑鼠或是鍵盤事件。但要注意的是,此模式沒有保證請求一定會被處理,端看設計者如何設計責任鏈。
參考資料:
深入淺出設計模式(Head First Design Patterns)
Chain of Responsibility 模式
[.NET]重構之路系列v11 –用責任鏈模式打破討厭的switch case
讓多個物件都有機會可以處理請求,以避免請求的發送者和接受者之間產生耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞請求,直到有一個物件處理這個請求為止。
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
從類別圖來看這個模式很單純,每個子類別都有要實作的方法來處理它們能處理的特殊請求。當有不能處理的請求時,也能傳給下一個子類別繼續處理。當然這邊就是由設計者自行決定要有幾個子類別來處理哪些不同的請求,以及決定這些子類別傳遞請求的順序為何。
接著來看一下套用責任鏈模式的簡單程式碼吧
// 所有處理郵件的子類別要繼承的介面 public abstract class MailHandler { // 每個處理郵件類別都要記錄下一個 // 能處理的人是誰 protected MailHandler mHandler; public MailHandler(MailHandler handler) { mHandler = handler; } public void toNext(Mail mail) { if(mHandler != null) { mHandler.handleMail(mail); } else { // 沒有後繼者, 表示是尾端了 // 通常可以用最一般化的處理 } } public abstract void handleMail(Mail mail); } public class SpamMailHandler extneds MailHandler { @Override public void handleMail(Mail mail) { // 假設已經能知道郵件分類了 if(mail.isSpam()) { // 處理垃圾信 } else { toNext(mail) } } } public class ThankMailHandler extneds MailHandler { ... } public class ComplainMailHandler extneds MailHandler { ... } public class GeneralMailHandler extends MailHandler { ... } // 可以這樣使用 MailHandler handler = new ThankMailHandler( new ComplainMailHandler( new SpamMailHandler( new GeneralMailHandler(null)))); // 假設有一封郵件要處理 Mail mail = new Mail(...); handler.hanldeMail(mail);
從程式碼可以看到,我們在建構 MailHandler 物件時,同時也指定要接在之後處理的物件為何,當然這個順序是可以自定義的。通常都是由小範圍到大範圍,特殊情形至一般情形來組織責任鏈,如上例把 GeneralMailHandler 放在最後面。
另外可能有人會覺得責任鏈物件生成的方式很像裝飾者模式,但這兩種模式意義卻是完全不同的喔。裝飾者模式的主要用途在增加元件的行為,而責任鏈模式是在組織處理請求的元件,範例中生成元件的部份也同時在指定元件順序,並沒有增加元件的行為。
責任鏈模式可以將請求的發出者和接受者之間予以鬆綁。請求者不需要知道實際接受者是誰,也不用知道請求是如何被處理,各個接受者之間也是彼此獨立且鬆綁的。責任鏈模式也可以動態修改責任鏈,如新增或刪除處理請求的物件。因為上述的特性,因此很常用來使用在視窗程式中,處理像是滑鼠或是鍵盤事件。但要注意的是,此模式沒有保證請求一定會被處理,端看設計者如何設計責任鏈。
參考資料:
深入淺出設計模式(Head First Design Patterns)
Chain of Responsibility 模式
[.NET]重構之路系列v11 –用責任鏈模式打破討厭的switch case
留言
張貼留言