日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

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

發(fā)布時(shí)間:2024/9/5 asp.net 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 重温设计模式(三)——职责链模式(chain of responsibility) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一. 寫(xiě)在前面的

這么多的設(shè)計(jì)模式,我覺(jué)得職責(zé)鏈?zhǔn)俏业谝淮慰瓷先プ詈?jiǎn)單,可是回想起來(lái)卻又最復(fù)雜的一個(gè)模式。

因此,這個(gè)文章我醞釀了很久,一直也沒(méi)有膽量發(fā)出來(lái),例子也是改了又改,可是仍然覺(jué)得不夠合理。所以希望各位多多指教。

二. 什么是鏈

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

我給鏈下了這樣的定義:

1. 鏈?zhǔn)且幌盗泄?jié)點(diǎn)的集合。

2. 鏈的各節(jié)點(diǎn)可靈活拆分再重組。

三. 何為職責(zé)鏈

職責(zé)鏈模式:使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。將這個(gè)對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理他為止。

圖如下:

UML很簡(jiǎn)單,讓我們先來(lái)看一個(gè)簡(jiǎn)單的例子。

四. 職責(zé)鏈模式應(yīng)用之請(qǐng)假管理

請(qǐng)假這個(gè)事情,相信每個(gè)人都不陌生。

我們公司是個(gè)相對(duì)很寬松的公司。

在公司里,如果你的請(qǐng)假時(shí)間小于0.5天,那么只需要向項(xiàng)目經(jīng)理打聲招呼就OK了。

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

如果超過(guò)了2天,你就需要去找總經(jīng)理了,工資當(dāng)然也玩完了。

那么,對(duì)于我們來(lái)說(shuō),這個(gè)流程就是這樣的。

也就是這樣一個(gè)過(guò)程,你需要和你的直接上級(jí)——項(xiàng)目經(jīng)理去打交道,最終可能是項(xiàng)目經(jīng)理給你回郵件,可能是人事部給你回郵件,也可能是總經(jīng)理給你回郵件。內(nèi)部的過(guò)程其實(shí)應(yīng)該是個(gè)黑盒子,你并不知道內(nèi)部的消息是如何處理的。你需要找到的,只是你想要第一個(gè)交付的對(duì)象而已。

那么我們的代碼應(yīng)該是這樣的。

首先我們要寫(xiě)一個(gè)請(qǐng)求的類(lèi)。

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("正當(dāng)理由")){return true;}return false;} }

那么我們調(diào)用的時(shí)候就很簡(jiǎn)單了!

static void Main(string[] args) {Request request = new Request(3, "非正當(dāng)理由");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); }

五. 靈活在哪?

讓我們來(lái)看下職責(zé)鏈究竟靈活在哪?

1. 改變內(nèi)部的傳遞規(guī)則。

在內(nèi)部,項(xiàng)目經(jīng)理完全可以跳過(guò)人事部到那一關(guān)直接找到總經(jīng)理。

每個(gè)人都可以去動(dòng)態(tài)地指定他的繼任者。

2. 可以從職責(zé)鏈任何一關(guān)開(kāi)始。

如果項(xiàng)目經(jīng)理不在,那么完全可以寫(xiě)這樣的代碼:

static void Main(string[] args) {Request request = new Request(3, "非正當(dāng)理由");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. 我們來(lái)比較一下,用職責(zé)鏈和不用職責(zé)鏈的區(qū)別:

這是不用職責(zé)鏈我們的結(jié)構(gòu),我們需要和公司中的每一個(gè)層級(jí)都發(fā)生耦合關(guān)系。

如果反映在代碼上即使我們需要在一個(gè)類(lèi)中去寫(xiě)上很多丑陋的if….else語(yǔ)句。

如果用了職責(zé)鏈,相當(dāng)于我們面對(duì)的是一個(gè)黑箱,我們只需要認(rèn)識(shí)其中的一個(gè)部門(mén),然后讓黑箱內(nèi)部去負(fù)責(zé)傳遞就好了。

六. 職責(zé)鏈 != 鏈表

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

他們區(qū)別在哪里:

讓我們看一個(gè)鏈表的典型結(jié)構(gòu):

讓我們來(lái)看一下鏈表的典型特征:

1. 鏈表是一個(gè)鏈狀結(jié)構(gòu),每個(gè)節(jié)點(diǎn)有一個(gè)next屬性去指向他的下一節(jié)點(diǎn)。

2. 鏈表有一個(gè)Header節(jié)點(diǎn),然后用戶(hù)每次必須通過(guò)頭節(jié)點(diǎn),然后去遍歷尋找每一個(gè)節(jié)點(diǎn)。

3. 鏈表遍歷操作的復(fù)雜度是O(n),但是插入和刪除指定節(jié)點(diǎn)的復(fù)雜度是常數(shù)級(jí)。

讓我們來(lái)著重看這第二點(diǎn):

我們來(lái)想想在文章開(kāi)始時(shí)我們畫(huà)出的那個(gè)鏈,一個(gè)鏈,我們可以從頭將他拿起,也可以從中間將他拿起:

也就是說(shuō)我們用戶(hù)可以去訪問(wèn)節(jié)點(diǎn)中的任何一個(gè)節(jié)點(diǎn)作為開(kāi)始節(jié)點(diǎn),這就是鏈表與職責(zé)鏈不同的地方。

七. 職責(zé)鏈的擴(kuò)展——樹(shù)狀鏈結(jié)構(gòu)

職責(zé)鏈中,我們之前看到的都是一些單鏈結(jié)構(gòu),但是其實(shí)在很多情況下,每一個(gè)節(jié)點(diǎn)都對(duì)應(yīng)著很多其他的部分。

?

那么這樣,我們的每一個(gè)節(jié)點(diǎn)都可以使用一個(gè)List來(lái)維護(hù)他節(jié)點(diǎn)的下一節(jié)點(diǎn),甚至可以用組合模式來(lái)分別設(shè)計(jì)每一節(jié)點(diǎn)。

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

仔細(xì)想想法律條文,尤其是刑法,經(jīng)常可以看到這樣的條文:

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

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

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

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

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

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

在我們的職責(zé)鏈中,如果不存在這樣的兜底條款,那么用戶(hù)如果不從首節(jié)點(diǎn)開(kāi)始訪問(wèn),那么就很可能出現(xiàn)異常的情況。于是我們應(yīng)該為職責(zé)鏈設(shè)置一個(gè)默認(rèn)的條款:

這樣的話,任何一個(gè)處理無(wú)論如何訪問(wèn),都能得到一個(gè)正常的處理。

九. 職責(zé)鏈的缺點(diǎn)

讓我們繼續(xù)回到上面的例子,我們發(fā)現(xiàn),其實(shí)當(dāng)請(qǐng)假時(shí)間超過(guò)2天的時(shí)候,PM和HR其實(shí)沒(méi)有做任何的事情,而只是做了一個(gè)傳遞工作。

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

也就是說(shuō),他們?cè)趯?shí)際的處理中,并沒(méi)有發(fā)揮任何的作用。

那么當(dāng)這個(gè)鏈結(jié)構(gòu)比較長(zhǎng),比較復(fù)雜的話,會(huì)產(chǎn)生很多的內(nèi)存垃圾對(duì)象。

這也就是職責(zé)鏈的最大缺點(diǎn)之所在。

十. 職責(zé)鏈的亂用

在和其他的人的討論中,我發(fā)現(xiàn)他們的觀點(diǎn)是:

只要一者傳一者,那么就要用職責(zé)鏈。在我們的項(xiàng)目中,他們這樣去用:

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();} }

這樣的話,每個(gè)類(lèi)相當(dāng)于只負(fù)責(zé)一個(gè)操作。

那么我們?nèi)绾胃倪M(jìn)呢?第一,我們可以用一個(gè)工廠來(lái)實(shí)現(xiàn)。另外,我們可以用表驅(qū)動(dòng)的方式來(lái)解決問(wèn)題。

十一. 表驅(qū)動(dòng)改進(jìn)職責(zé)鏈

表驅(qū)動(dòng)(Table driven),其實(shí)就是指用查表的方式來(lái)獲取值。

那么我們用標(biāo)驅(qū)動(dòng)法來(lái)改進(jìn)上面的例子:

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;} }

我想一個(gè)沒(méi)有學(xué)過(guò)設(shè)計(jì)模式的人都會(huì)這樣寫(xiě)的。一個(gè)學(xué)過(guò)設(shè)計(jì)模式很多年的人也會(huì)這樣寫(xiě)的。

而怕的就是為了模式而模式,為了職責(zé)鏈而職責(zé)鏈了。

十二. 職責(zé)鏈在java script中的應(yīng)用

我們想象這樣一種情況:

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

這其實(shí)就是一種職責(zé)鏈的體現(xiàn)!

十三. 深析職責(zé)鏈的使用

職責(zé)鏈模式不能亂用,否則非常容易變成因?yàn)槟J蕉J降姆蠢?/font>

下面是我歸納出來(lái)的一些關(guān)于職責(zé)鏈方面的使用規(guī)則,只是個(gè)人的意見(jiàn),還希望大家指教。

1, 如果存在N對(duì)N,或者是一般的常規(guī)線性關(guān)系,那么我們完全可以用表驅(qū)動(dòng)來(lái)取代職責(zé)鏈。

2, 對(duì)象本身要經(jīng)過(guò)什么處理是通過(guò)每個(gè)鏈上元素通過(guò)運(yùn)行態(tài)來(lái)決定的,決定的因素是取決于對(duì)象的屬性或者一些其他方面的策略。

3, 用戶(hù)無(wú)論是從哪一個(gè)節(jié)點(diǎn)作為他的請(qǐng)求頭節(jié)點(diǎn),最終用戶(hù)都可以得到一個(gè)請(qǐng)求的反饋。

4, 應(yīng)怪怪建議,補(bǔ)充同級(jí)的處理!職責(zé)鏈并非是嚴(yán)格的上下級(jí)的傳遞,其中也包括同級(jí)的傳遞,職責(zé)鏈一樣可以在同級(jí)之間做傳遞。

例如,繼續(xù)用我們上面請(qǐng)假的那個(gè)做例子,也許我們公司有兩個(gè)HR,事實(shí)上也是這樣的,我們把前臺(tái)“MM”也美稱(chēng)為人力資源部:

static void Main(string[] args) {Request request = new Request(3, "非正當(dāng)理由");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); }

其實(shí)這樣也未嘗不可。有人也許會(huì)說(shuō),那么這樣的同樣一個(gè)類(lèi)的兩個(gè)對(duì)象又有什么意義呢?

那么我們不妨去試著這樣改造這個(gè)HR的類(lèi)。

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);} }

這樣,因?yàn)榍芭_(tái)MM容易說(shuō)話,很可能他就不去扣你的工資,如果你去先找的HR,那么你這天的工資就報(bào)銷(xiāo)了。

同理,我們一樣可以讓他們的職責(zé)細(xì)化,比如說(shuō)Real Hr負(fù)責(zé)0.5天到1天的,而Qiantai去負(fù)責(zé)1天到2天的,也未嘗不可。

總之,職責(zé)鏈并非是單一的上下級(jí)的傳遞,一樣可以實(shí)現(xiàn)同級(jí)的傳遞。

十四. 職責(zé)鏈總結(jié)

職責(zé)鏈?zhǔn)鞘苟鄠€(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。將這個(gè)對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理他為止。

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

轉(zhuǎn)載于:https://www.cnblogs.com/kym/archive/2009/04/06/1430078.html

總結(jié)

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

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。