C#設(shè)計(jì)模式—責(zé)任鏈模式
一、引言
在現(xiàn)實(shí)生活中,有很多請(qǐng)求并不是一個(gè)人說(shuō)了就算的,例如面試時(shí)的工資,低于1萬(wàn)的薪水可能技術(shù)經(jīng)理就可以決定了,但是1萬(wàn)~1萬(wàn)5的薪水可能技術(shù)經(jīng)理就沒(méi)這個(gè)權(quán)利批準(zhǔn),可能就需要請(qǐng)求技術(shù)總監(jiān)的批準(zhǔn),所以在面試的完后,經(jīng)常會(huì)有面試官說(shuō),你這個(gè)薪水我這邊覺(jué)得你這技術(shù)可以拿這個(gè)薪水的,但是還需要技術(shù)總監(jiān)的批準(zhǔn)等的話(huà)。這個(gè)例子也就詮釋了本文要介紹的內(nèi)容。生活中的這個(gè)例子真是應(yīng)用了責(zé)任鏈模式。
二、責(zé)任鏈模式介紹
2.1 責(zé)任鏈模式的定義
從生活中的例子可以發(fā)現(xiàn),某個(gè)請(qǐng)求可能需要幾個(gè)人的審批,即使技術(shù)經(jīng)理審批完了,還需要上一級(jí)的審批。這樣的例子,還有公司中的請(qǐng)假,少于3天的,直屬Leader就可以批準(zhǔn),3天到7天之內(nèi)就需要項(xiàng)目經(jīng)理批準(zhǔn),多余7天的就需要技術(shù)總監(jiān)的批準(zhǔn)了。介紹了這么多生活中責(zé)任鏈模式的例子的,下面具體給出面向?qū)ο笾胸?zé)任鏈模式的定義。
責(zé)任鏈模式指的是——某個(gè)請(qǐng)求需要多個(gè)對(duì)象進(jìn)行處理,從而避免請(qǐng)求的發(fā)送者和接收之間的耦合關(guān)系。將這些對(duì)象連成一條鏈子,并沿著這條鏈子傳遞該請(qǐng)求,直到有對(duì)象處理它為止。
2.2 責(zé)任鏈模式的結(jié)構(gòu)圖
從責(zé)任鏈模式的定義可以發(fā)現(xiàn),責(zé)任鏈模式涉及的對(duì)象只有處理者角色,但由于有多個(gè)處理者,它們具有共同的處理請(qǐng)求的方法,所以這里抽象出一個(gè)抽象處理者角色進(jìn)行代碼復(fù)用。這樣分析下來(lái),責(zé)任鏈模式的結(jié)構(gòu)圖也就不言而喻了,具體結(jié)構(gòu)圖如下所示。

主要涉及兩個(gè)角色:
·?抽象處理者角色(Handler):定義出一個(gè)處理請(qǐng)求的接口。這個(gè)接口通常由接口或抽象類(lèi)來(lái)實(shí)現(xiàn)。
·?具體處理者角色(ConcreteHandler):具體處理者接受到請(qǐng)求后,可以選擇將該請(qǐng)求處理掉,或者將請(qǐng)求傳給下一個(gè)處理者。因此,每個(gè)具體處理者需要保存下一個(gè)處理者的引用,以便把請(qǐng)求傳遞下去。
2.3 責(zé)任鏈模式的實(shí)現(xiàn)
有了上面的介紹,下面以公司采購(gòu)東西為例子來(lái)實(shí)現(xiàn)責(zé)任鏈模式。公司規(guī)定,采購(gòu)架構(gòu)總價(jià)在1萬(wàn)之內(nèi),經(jīng)理級(jí)別的人批準(zhǔn)即可,總價(jià)大于1萬(wàn)小于2萬(wàn)5的則還需要副總進(jìn)行批準(zhǔn),總價(jià)大于2萬(wàn)5小于10萬(wàn)的需要還需要總經(jīng)理批準(zhǔn),而大于總價(jià)大于10萬(wàn)的則需要組織一個(gè)會(huì)議進(jìn)行討論。對(duì)于這樣一個(gè)需求,最直觀的方法就是設(shè)計(jì)一個(gè)方法,參數(shù)是采購(gòu)的總價(jià),然后在這個(gè)方法內(nèi)對(duì)價(jià)格進(jìn)行調(diào)整判斷,然后針對(duì)不同的條件交給不同級(jí)別的人去處理,這樣確實(shí)可以解決問(wèn)題,但這樣一來(lái),我們就需要多重if-else語(yǔ)句來(lái)進(jìn)行判斷,但當(dāng)加入一個(gè)新的條件范圍時(shí),我們又不得不去修改原來(lái)設(shè)計(jì)的方法來(lái)再添加一個(gè)條件判斷,這樣的設(shè)計(jì)顯然違背了“開(kāi)-閉”原則。這時(shí)候,可以采用責(zé)任鏈模式來(lái)解決這樣的問(wèn)題。具體實(shí)現(xiàn)代碼如下所示。
?
namespace?ChainofResponsibility
{
????// 采購(gòu)請(qǐng)求
????public?class?PurchaseRequest
????{
????????// 金額
????????public?double?Amount { get; set; }
????????// 產(chǎn)品名字
????????public?string?ProductName { get; set; }
????????public?PurchaseRequest(double?amount, string?productName)
????????{
????????????Amount = amount;
????????????ProductName = productName;
????????}
????}
?
????// 審批人,Handler
????public?abstract?class?Approver
????{
????????public?Approver NextApprover { get; set; }
????????public?string?Name { get; set; }
????????public?Approver(string?name)
????????{
????????????this.Name = name;
????????}
????????public?abstract?void?ProcessRequest(PurchaseRequest request);
????}
?
????// ConcreteHandler
????public?class?Manager : Approver
????{
????????public?Manager(string?name)
????????????: base(name)
????????{ }
?
????????public?override?void?ProcessRequest(PurchaseRequest request)
????????{
????????????if?(request.Amount < 10000.0)
????????????{
????????????????Console.WriteLine("{0}-{1} approved the request of purshing {2}", this, Name, request.ProductName);
????????????}
????????????else?if?(NextApprover != null)
????????????{
????????????????NextApprover.ProcessRequest(request);
????????????}
????????}
????}
?
????// ConcreteHandler,副總
????public?class?VicePresident : Approver
????{
????????public?VicePresident(string?name)
????????????: base(name)
????????{
????????}
????????public?override?void?ProcessRequest(PurchaseRequest request)
????????{
????????????if?(request.Amount < 25000.0)
????????????{
????????????????Console.WriteLine("{0}-{1} approved the request of purshing {2}", this, Name, request.ProductName);
????????????}
????????????else?if?(NextApprover != null)
????????????{
????????????????NextApprover.ProcessRequest(request);
????????????}
????????}
????}
?
????// ConcreteHandler,總經(jīng)理
????public?class?President :Approver
????{
????????public?President(string?name)
????????????: base(name)
????????{ }
????????public?override?void?ProcessRequest(PurchaseRequest request)
????????{
????????????if?(request.Amount < 100000.0)
????????????{
????????????????Console.WriteLine("{0}-{1} approved the request of purshing {2}", this, Name, request.ProductName);
????????????}
????????????else
????????????{
????????????????Console.WriteLine("Request需要組織一個(gè)會(huì)議討論");
????????????}
????????}
????}
?
????class?Program
????{
????????static?void?Main(string[] args)
????????{
????????????PurchaseRequest requestTelphone = new?PurchaseRequest(4000.0, "Telphone");
????????????PurchaseRequest requestSoftware = new?PurchaseRequest(10000.0, "Visual Studio");
????????????PurchaseRequest requestComputers = new?PurchaseRequest(40000.0, "Computers");
?
????????????Approver manager = new?Manager("LearningHard");
????????????Approver Vp = new?VicePresident("Tony");
????????????Approver Pre = new?President("BossTom");
?
????????????// 設(shè)置責(zé)任鏈
????????????manager.NextApprover = Vp;
????????????Vp.NextApprover = Pre;
?
????????????// 處理請(qǐng)求????????????
? ? ? ? ? ? manager.ProcessRequest(requestTelphone);
????????????manager.ProcessRequest(requestSoftware);
????????????manager.ProcessRequest(requestComputers);
????????????Console.ReadLine();
????????}
????}
}
既然,原來(lái)的設(shè)計(jì)會(huì)因?yàn)閮r(jià)格條件范圍的變化而導(dǎo)致不利于擴(kuò)展,根據(jù)“封裝變化”的原則,此時(shí)我們想的自然是能不能把價(jià)格范圍細(xì)化到不同的類(lèi)中呢?因?yàn)槊總€(gè)價(jià)格范圍都決定某個(gè)批準(zhǔn)者,這里就聯(lián)想到創(chuàng)建多個(gè)批準(zhǔn)類(lèi),這樣每個(gè)類(lèi)中只需要針對(duì)他自己這個(gè)范圍的價(jià)格判斷。這樣也就是責(zé)任鏈的最后實(shí)現(xiàn)方式了,具體的運(yùn)行結(jié)果如下圖所示。

三、責(zé)任鏈模式的適用場(chǎng)景?
在以下場(chǎng)景中可以考慮使用責(zé)任鏈模式:
·?一個(gè)系統(tǒng)的審批需要多個(gè)對(duì)象才能完成處理的情況下,例如請(qǐng)假系統(tǒng)等。
·?代碼中存在多個(gè)if-else語(yǔ)句的情況下,此時(shí)可以考慮使用責(zé)任鏈模式來(lái)對(duì)代碼進(jìn)行重構(gòu)。
四、責(zé)任鏈模式的優(yōu)缺點(diǎn)
責(zé)任鏈模式的優(yōu)點(diǎn)不言而喻,主要有以下點(diǎn):
·?降低了請(qǐng)求的發(fā)送者和接收者之間的耦合。
·?把多個(gè)條件判定分散到各個(gè)處理類(lèi)中,使得代碼更加清晰,責(zé)任更加明確。
責(zé)任鏈模式也具有一定的缺點(diǎn),如:
·?在找到正確的處理對(duì)象之前,所有的條件判定都要執(zhí)行一遍,當(dāng)責(zé)任鏈過(guò)長(zhǎng)時(shí),可能會(huì)引起性能的問(wèn)題
·?可能導(dǎo)致某個(gè)請(qǐng)求不被處理。
五、總結(jié)
責(zé)任鏈降低了請(qǐng)求端和接收端之間的耦合,使多個(gè)對(duì)象都有機(jī)會(huì)處理某個(gè)請(qǐng)求。如考試中作弊傳紙條,泡妞傳情書(shū)一般。?
