日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

重温设计模式(三)——职责链模式(chain of responsibility)

發布時間:2024/9/5 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 重温设计模式(三)——职责链模式(chain of responsibility) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一. 寫在前面的

這么多的設計模式,我覺得職責鏈是我第一次看上去最簡單,可是回想起來卻又最復雜的一個模式。

因此,這個文章我醞釀了很久,一直也沒有膽量發出來,例子也是改了又改,可是仍然覺得不夠合理。所以希望各位多多指教。

二. 什么是鏈

文章伊始,先讓我們了解這個最基本的概念,什么是鏈。

我給鏈下了這樣的定義:

1. 鏈是一系列節點的集合。

2. 鏈的各節點可靈活拆分再重組。

三. 何為職責鏈

職責鏈模式:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系。將這個對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理他為止。

圖如下:

UML很簡單,讓我們先來看一個簡單的例子。

四. 職責鏈模式應用之請假管理

請假這個事情,相信每個人都不陌生。

我們公司是個相對很寬松的公司。

在公司里,如果你的請假時間小于0.5天,那么只需要向項目經理打聲招呼就OK了。

如果超過了0.5天,但是還小于2天,那么就要去找人事部處理,當然,這就要扣工資了。

如果超過了2天,你就需要去找總經理了,工資當然也玩完了。

那么,對于我們來說,這個流程就是這樣的。

也就是這樣一個過程,你需要和你的直接上級——項目經理去打交道,最終可能是項目經理給你回郵件,可能是人事部給你回郵件,也可能是總經理給你回郵件。內部的過程其實應該是個黑盒子,你并不知道內部的消息是如何處理的。你需要找到的,只是你想要第一個交付的對象而已。

那么我們的代碼應該是這樣的。

首先我們要寫一個請求的類。

class Request {private int day;private string reason;public int Day{get { return day; }set { day = value; }}public string Reason{get { return reason; }set { reason = value; }}public Request(int day, string reason){this.day = day;this.reason = reason;} }abstract class Boss {private string name;public string Name{get { return name; }set { name = value; }}private Boss successor;public Boss Successor{get { return successor; }set { successor = value; }}public Boss(string name){this.name = name;}public abstract bool PassRequest(Request request); } class PM:Boss {public PM(string name): base(name){ }public override bool PassRequest(Request request){int day = request.Day;string reason = request.Reason;if (day <= 0.5){return true;}return Successor.PassRequest(request);} } class HR:Boss {public HR(string name): base(name){ }public override bool PassRequest(Request request){int day = request.Day;string reason = request.Reason;if (day > 0.5&&day<=2){return true;}return Successor.PassRequest(request);} } class Manager : Boss {public Manager(string name): base(name){ }public override bool PassRequest(Request request){int day = request.Day;string reason = request.Reason;if (reason.Equals("正當理由")){return true;}return false;} }

那么我們調用的時候就很簡單了!

static void Main(string[] args) {Request request = new Request(3, "非正當理由");Boss pm = new PM("pm");Boss hr = new HR("hr");Boss manager = new Manager("manager");pm.Successor = hr;hr.Successor = manager;bool pass = pm.PassRequest(request);Console.Write(pass); }

五. 靈活在哪?

讓我們來看下職責鏈究竟靈活在哪?

1. 改變內部的傳遞規則。

在內部,項目經理完全可以跳過人事部到那一關直接找到總經理。

每個人都可以去動態地指定他的繼任者。

2. 可以從職責鏈任何一關開始。

如果項目經理不在,那么完全可以寫這樣的代碼:

static void Main(string[] args) {Request request = new Request(3, "非正當理由");Boss pm = new PM("pm");Boss hr = new HR("hr");Boss manager = new Manager("manager");pm.Successor = hr;hr.Successor = manager;//bool pass = pm.PassRequest(request);bool pass = hr.PassRequest(request);Console.Write(pass); }

3. 我們來比較一下,用職責鏈和不用職責鏈的區別:

這是不用職責鏈我們的結構,我們需要和公司中的每一個層級都發生耦合關系。

如果反映在代碼上即使我們需要在一個類中去寫上很多丑陋的if….else語句。

如果用了職責鏈,相當于我們面對的是一個黑箱,我們只需要認識其中的一個部門,然后讓黑箱內部去負責傳遞就好了。

六. 職責鏈 != 鏈表

很多人都愿意把職責鏈和鏈表混為一談,確實,從字面意思上理解,鏈,鏈表,很像。可是他們一樣么?

他們區別在哪里:

讓我們看一個鏈表的典型結構:

讓我們來看一下鏈表的典型特征:

1. 鏈表是一個鏈狀結構,每個節點有一個next屬性去指向他的下一節點。

2. 鏈表有一個Header節點,然后用戶每次必須通過頭節點,然后去遍歷尋找每一個節點。

3. 鏈表遍歷操作的復雜度是O(n),但是插入和刪除指定節點的復雜度是常數級。

讓我們來著重看這第二點:

我們來想想在文章開始時我們畫出的那個鏈,一個鏈,我們可以從頭將他拿起,也可以從中間將他拿起:

也就是說我們用戶可以去訪問節點中的任何一個節點作為開始節點,這就是鏈表與職責鏈不同的地方。

七. 職責鏈的擴展——樹狀鏈結構

職責鏈中,我們之前看到的都是一些單鏈結構,但是其實在很多情況下,每一個節點都對應著很多其他的部分。

?

那么這樣,我們的每一個節點都可以使用一個List來維護他節點的下一節點,甚至可以用組合模式來分別設計每一節點。

八. 由法律想到——職責鏈的兜底條款

仔細想想法律條文,尤其是刑法,經常可以看到這樣的條文:

1. 如果*********,則處以拘役處分。

2. 如果*********,則處以有期徒刑一年到十年。

3. 如果*********,則處以有期徒刑十年以上。

4. 如果*********,則**********。

5. 如果以上條件皆不滿足,則*****************。

其實最后一條就叫做法律的兜底條款。這給了法官很大的自由裁量權,在一定程度上也降低了犯罪分子鉆法律空子的可能性。

在我們的職責鏈中,如果不存在這樣的兜底條款,那么用戶如果不從首節點開始訪問,那么就很可能出現異常的情況。于是我們應該為職責鏈設置一個默認的條款:

這樣的話,任何一個處理無論如何訪問,都能得到一個正常的處理。

九. 職責鏈的缺點

讓我們繼續回到上面的例子,我們發現,其實當請假時間超過2天的時候,PM和HR其實沒有做任何的事情,而只是做了一個傳遞工作。

而傳遞工作之后,他們就成了垃圾對象。

也就是說,他們在實際的處理中,并沒有發揮任何的作用。

那么當這個鏈結構比較長,比較復雜的話,會產生很多的內存垃圾對象。

這也就是職責鏈的最大缺點之所在。

十. 職責鏈的亂用

在和其他的人的討論中,我發現他們的觀點是:

只要一者傳一者,那么就要用職責鏈。在我們的項目中,他們這樣去用:

abstract class DBHelper { }interface IRequestHandler {IDBHelper ReturnHelper(string dbName); } class RequestHandler:IRequestHandler {private RequestHandler successor;public RequestHandler Successor{get { return successor; }set { successor = value; }}public abstract IDBHelper ReturnHelper(string dbName); }class SQLHelper : DBHelper { } class OracleHelper : DBHelper { } class DB2Helper : DBHelper { } class SQL : RequestHandler {public override IDBHelper ReturnHelper(string dbName){if (dbName.Equals("SQL Server")){return new SQLHelper();}return Successor.ReturnHelper(dbName);} } class Oracle : RequestHandler {public override IDBHelper ReturnHelper(string dbName){if (dbName.Equals("Oracle")){return new OracleHelper();}return Successor.ReturnHelper(dbName);} } class DB2 : RequestHandler {public override IDBHelper ReturnHelper(string dbName){if (dbName.Equals("DB2")){return new DB2Helper();}return new SQLHelper();} }

這樣的話,每個類相當于只負責一個操作。

那么我們如何改進呢?第一,我們可以用一個工廠來實現。另外,我們可以用表驅動的方式來解決問題。

十一. 表驅動改進職責鏈

表驅動(Table driven),其實就是指用查表的方式來獲取值。

那么我們用標驅動法來改進上面的例子:

class HelperRequest {private Dictionary<String, DBHelper> dic = new Dictionary<string, DBHelper>();public void Add(string name,DBHelper helper){dic.Add(name, helper);}public DBHelper GetHelper(string name){DBHelper helper;bool temp = dic.TryGetValue(name, out helper);if (temp){return helper;}return null;} }

我想一個沒有學過設計模式的人都會這樣寫的。一個學過設計模式很多年的人也會這樣寫的。

而怕的就是為了模式而模式,為了職責鏈而職責鏈了。

十二. 職責鏈在java script中的應用

我們想象這樣一種情況:

我們都知道,在ASP.NET 的 Webform模型中頁面是以控件樹的形式去組織的。那么我們用右鍵點擊其中的一個頁面,那么這個事件就會找離他最近的控件,如果不存在,那么就去找他的父控件,如此遞歸下去,直到找到為止。

這其實就是一種職責鏈的體現!

十三. 深析職責鏈的使用

職責鏈模式不能亂用,否則非常容易變成因為模式而模式的反例。

下面是我歸納出來的一些關于職責鏈方面的使用規則,只是個人的意見,還希望大家指教。

1, 如果存在N對N,或者是一般的常規線性關系,那么我們完全可以用表驅動來取代職責鏈。

2, 對象本身要經過什么處理是通過每個鏈上元素通過運行態來決定的,決定的因素是取決于對象的屬性或者一些其他方面的策略。

3, 用戶無論是從哪一個節點作為他的請求頭節點,最終用戶都可以得到一個請求的反饋。

4, 應怪怪建議,補充同級的處理!職責鏈并非是嚴格的上下級的傳遞,其中也包括同級的傳遞,職責鏈一樣可以在同級之間做傳遞。

例如,繼續用我們上面請假的那個做例子,也許我們公司有兩個HR,事實上也是這樣的,我們把前臺“MM”也美稱為人力資源部:

static void Main(string[] args) {Request request = new Request(3, "非正當理由");Boss pm = new PM("pm");Boss hr1 = new HR("Real HR");Boss hr2 = new HR("QiantaiMM");Boss manager = new Manager("manager");pm.Successor = hr1;hr1.Successor = hr2;hr2.Successor = manager;bool pass = pm.PassRequest(request);Console.Write(pass); }

其實這樣也未嘗不可。有人也許會說,那么這樣的同樣一個類的兩個對象又有什么意義呢?

那么我們不妨去試著這樣改造這個HR的類。

enum HRType {RealHR,Qiantai } class HR:Boss {private HRType type;public HR(string name,HRType type): base(name){this.type = type;}public override bool PassRequest(Request request){int day = request.Day;if (day>=0.5&&day<2){switch (type){ case HRType.RealHR://扣工資return true;break;case HRType.Qiantai://不扣工資return true;break;}}return Successor.PassRequest(request);} }

這樣,因為前臺MM容易說話,很可能他就不去扣你的工資,如果你去先找的HR,那么你這天的工資就報銷了。

同理,我們一樣可以讓他們的職責細化,比如說Real Hr負責0.5天到1天的,而Qiantai去負責1天到2天的,也未嘗不可。

總之,職責鏈并非是單一的上下級的傳遞,一樣可以實現同級的傳遞。

十四. 職責鏈總結

職責鏈是使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系。將這個對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理他為止。

今天就寫到這了,希望大家多多指教。

轉載于:https://www.cnblogs.com/kym/archive/2009/04/06/1430078.html

總結

以上是生活随笔為你收集整理的重温设计模式(三)——职责链模式(chain of responsibility)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。