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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

请求的链式处理——职责链模式

發布時間:2024/2/28 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 请求的链式处理——职责链模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文轉載自 :http://blog.csdn.net/lovelion/article/details/7420891


“一對二”,“過”,“過”……這聲音熟悉嗎?你會想到什么?對!紙牌。在類似“斗地主”這樣的紙牌游戲中,某人出牌給他的下家,下家看看手中的牌,如果要不起上家的牌則將出牌請求再轉發給他的下家,其下家再進行判斷。一個循環下來,如果其他人都要不起該牌,則最初的出牌者可以打出新的牌。在這個過程中,牌作為一個請求沿著一條鏈在傳遞,每一位紙牌的玩家都可以處理該請求。在設計模式中,我們也有一種專門用于處理這種請求鏈式傳遞的模式,它就是本章將要介紹的職責鏈模式。

?

16.1?采購單的分級審批

????? Sunny軟件公司承接了某企業SCM(Supply Chain Management,供應鏈管理)系統的開發任務,其中包含一個采購審批子系統。該企業的采購審批是分級進行的,即根據采購金額的不同由不同層次的主管人員來審批,主任可以審批5萬元以下(不包括5萬元)的采購單,副董事長可以審批5萬元至10萬元(不包括10萬元)的采購單,董事長可以審批10萬元至50萬元(不包括50萬元)的采購單,50萬元及以上的采購單就需要開董事會討論決定。如圖16-1所示:

16-1?采購單分級審批示意圖

??????如何在軟件中實現采購單的分級審批?Sunny軟件公司開發人員提出了一個初始解決方案,在系統中提供一個采購單處理類PurchaseRequestHandler用于統一處理采購單,其框架代碼如下所示:

[java]?view plaincopy
  • //采購單處理類??
  • class?PurchaseRequestHandler?{??
  • ????//遞交采購單給主任??
  • ????public?void?sendRequestToDirector(PurchaseRequest?request)?{??
  • ????????if?(request.getAmount()?<?50000)?{??
  • ????????????//主任可審批該采購單??
  • ????????????this.handleByDirector(request);??
  • ????????}??
  • ????????else?if?(request.getAmount()?<?100000)?{??
  • ????????????//副董事長可審批該采購單??
  • ????????????this.handleByVicePresident(request);??
  • ????????}??
  • ????????else?if?(request.getAmount()?<?500000)?{??
  • ????????????//董事長可審批該采購單??
  • ????????????this.handleByPresident(request);??
  • ????????}??
  • ????????else?{??
  • ????????????//董事會可審批該采購單??
  • ????????????this.handleByCongress(request);??
  • ????????}??
  • ????}??
  • ??????
  • ????//主任審批采購單??
  • ????public?void?handleByDirector(PurchaseRequest?request)?{??
  • ????????//代碼省略??
  • ????}??
  • ??????
  • ????//副董事長審批采購單??
  • ????public?void?handleByVicePresident(PurchaseRequest?request)?{??
  • ????????//代碼省略??
  • ????}??
  • ??????
  • ????//董事長審批采購單??
  • ????public?void?handleByPresident(PurchaseRequest?request)?{??
  • ????????//代碼省略??
  • ????}??
  • ??????
  • ????//董事會審批采購單??
  • ????public?void?handleByCongress(PurchaseRequest?request)?{??
  • ????????//代碼省略??
  • ????}??
  • }??
  • ??????? 問題貌似很簡單,但仔細分析,發現上述方案存在如下幾個問題:

    ?????? (1)PurchaseRequestHandler類較為龐大,各個級別的審批方法都集中在一個類中,違反了“單一職責原則”,測試和維護難度大。

    ?????? (2)如果需要增加一個新的審批級別或調整任何一級的審批金額和審批細節(例如將董事長的審批額度改為60萬元)時都必須修改源代碼并進行嚴格測試,此外,如果需要移除某一級別(例如金額為10萬元及以上的采購單直接由董事長審批,不再設副董事長一職)時也必須對源代碼進行修改,違反了“開閉原則”。

    ?????? (3)審批流程的設置缺乏靈活性,現在的審批流程是“主任-->副董事長-->董事長-->董事會”,如果需要改為“主任-->董事長-->董事會”,在此方案中只能通過修改源代碼來實現,客戶端無法定制審批流程。

    ???????如何針對上述問題對系統進行改進?Sunny公司開發人員迫切需要一種新的設計方案,還好有職責鏈模式,通過使用職責鏈模式我們可以最大程度地解決這些問題,下面讓我們正式進入職責鏈模式的學習。

    16.2 職責鏈模式概述

    ????? 很多情況下,在一個軟件系統中可以處理某個請求的對象不止一個,例如SCM系統中的采購單審批,主任、副董事長、董事長和董事會都可以處理采購單,他們可以構成一條處理采購單的鏈式結構,采購單沿著這條鏈進行傳遞,這條鏈就稱為職責鏈。職責鏈可以是一條直線、一個環或者一個樹形結構,最常見的職責鏈是直線型,即沿著一條單向的鏈來傳遞請求。鏈上的每一個對象都是請求處理者,職責鏈模式可以將請求的處理者組織成一條鏈,并讓請求沿著鏈傳遞,由鏈上的處理者對請求進行相應的處理,客戶端無須關心請求的處理細節以及請求的傳遞,只需將請求發送到鏈上即可,實現請求發送者和請求處理者解耦。

    ????? 職責鏈模式定義如下

    職責鏈模式(Chain of Responsibility? Pattern):避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,并且沿著這條鏈傳遞請求,直到有對象處理它為止。職責鏈模式是一種對象行為型模式。

    ????? 職責鏈模式結構的核心在于引入了一個抽象處理者。職責鏈模式結構如圖16-2所示:

    ??????在職責鏈模式結構圖中包含如下幾個角色:

    ????? ●?Handler(抽象處理者):它定義了一個處理請求的接口,一般設計為抽象類,由于不同的具體處理者處理請求的方式不同,因此在其中定義了抽象請求處理方法。因為每一個處理者的下家還是一個處理者,因此在抽象處理者中定義了一個抽象處理者類型的對象(如結構圖中的successor),作為其對下家的引用。通過該引用,處理者可以連成一條鏈。

    ????? ●?ConcreteHandler(具體處理者):它是抽象處理者的子類,可以處理用戶請求,在具體處理者類中實現了抽象處理者中定義的抽象請求處理方法,在處理請求之前需要進行判斷,看是否有相應的處理權限,如果可以處理請求就處理它,否則將請求轉發給后繼者;在具體處理者中可以訪問鏈中下一個對象,以便請求的轉發。

    ????? 在職責鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端并不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織鏈和分配責任

    ????? 職責鏈模式的核心在于抽象處理者類的設計,抽象處理者的典型代碼如下所示:

    [java]?view plaincopy
  • abstract?class?Handler?{??
  • ????//維持對下家的引用??
  • protected?Handler?successor;??
  • ??????
  • ????public?void?setSuccessor(Handler?successor)?{??
  • ????????this.successor=successor;??
  • ????}??
  • ??????
  • ????public?abstract?void?handleRequest(String?request);??
  • }??
  • ?????? 上述代碼中,抽象處理者類定義了對下家的引用對象,以便將請求轉發給下家,該對象的訪問符可設為protected,在其子類中可以使用。在抽象處理者類中聲明了抽象的請求處理方法,具體實現交由子類完成。

    ??????? 具體處理者是抽象處理者的子類,它具有兩大作用:第一是處理請求,不同的具體處理者以不同的形式實現抽象請求處理方法handleRequest()第二是轉發請求,如果該請求超出了當前處理者類的權限,可以將該請求轉發給下家。具體處理者類的典型代碼如下:

    [java]?view plaincopy
  • class?ConcreteHandler?extends?Handler?{??
  • ????public?void?handleRequest(String?request)?{??
  • ????????if?(請求滿足條件)?{??
  • ????????????//處理請求??
  • ????????}??
  • ????????else?{??
  • ????????????this.successor.handleRequest(request);??//轉發請求??
  • ????????}??
  • ????}??
  • }??
  • ?????? 在具體處理類中通過對請求進行判斷可以做出相應的處理。

    ????????需要注意的是職責鏈模式并不創建職責鏈,職責鏈的創建工作必須由系統的其他部分來完成,一般是在使用該職責鏈的客戶端中創建職責鏈職責鏈模式降低了請求的發送端和接收端之間的耦合,使多個對象都有機會處理這個請求。?

    ?

    思考

    如何在客戶端創建一條職責鏈?


    16.3 完整解決方案

    ??????為了讓采購單的審批流程更加靈活,并實現采購單的鏈式傳遞和處理,Sunny公司開發人員使用職責鏈模式來實現采購單的分級審批,其基本結構如圖16-3所示:

    在圖16-3中,抽象類Approver充當抽象處理者(抽象傳遞者),DirectorVicePresidentPresidentCongress充當具體處理者(具體傳遞者),PurchaseRequest充當請求類。完整代碼如下所示: [java]?view plaincopy
  • //采購單:請求類??
  • class?PurchaseRequest?{??
  • ????private?double?amount;??//采購金額??
  • ????private?int?number;??//采購單編號??
  • ????private?String?purpose;??//采購目的??
  • ??????
  • ????public?PurchaseRequest(double?amount,?int?number,?String?purpose)?{??
  • ????????this.amount?=?amount;??
  • ????????this.number?=?number;??
  • ????????this.purpose?=?purpose;??
  • ????}??
  • ??????
  • ????public?void?setAmount(double?amount)?{??
  • ????????this.amount?=?amount;??
  • ????}??
  • ??????
  • ????public?double?getAmount()?{??
  • ????????return?this.amount;??
  • ????}??
  • ??????
  • ????public?void?setNumber(int?number)?{??
  • ????????this.number?=?number;??
  • ????}??
  • ??????
  • ????public?int?getNumber()?{??
  • ????????return?this.number;??
  • ????}??
  • ??????
  • ????public?void?setPurpose(String?purpose)?{??
  • ????????this.purpose?=?purpose;??
  • ????}??
  • ??????
  • ????public?String?getPurpose()?{??
  • ????????return?this.purpose;??
  • ????}??
  • }??
  • ??
  • //審批者類:抽象處理者??
  • abstract?class?Approver?{??
  • ????protected?Approver?successor;?//定義后繼對象??
  • ????protected?String?name;?//審批者姓名??
  • ??????
  • ????public?Approver(String?name)?{??
  • ????????this.name?=?name;??
  • ????}??
  • ??
  • ????//設置后繼者??
  • ????public?void?setSuccessor(Approver?successor)?{???
  • ????????this.successor?=?successor;??
  • ????}??
  • ??
  • ????//抽象請求處理方法??
  • ????public?abstract?void?processRequest(PurchaseRequest?request);??
  • }??
  • ??
  • //主任類:具體處理者??
  • class?Director?extends?Approver?{??
  • ????public?Director(String?name)?{??
  • ????????super(name);??
  • ????}??
  • ??????
  • ????//具體請求處理方法??
  • ????public?void?processRequest(PurchaseRequest?request)?{??
  • ????????if?(request.getAmount()?<?50000)?{??
  • ????????????System.out.println("主任"?+?this.name?+?"審批采購單:"?+?request.getNumber()?+?",金額:"?+?request.getAmount()?+?"元,采購目的:"?+?request.getPurpose()?+?"。");??//處理請求??
  • ????????}??
  • ????????else?{??
  • ????????????this.successor.processRequest(request);??//轉發請求??
  • ????????}?????
  • ????}??
  • }??
  • ??
  • //副董事長類:具體處理者??
  • class?VicePresident?extends?Approver?{??
  • ????public?VicePresident(String?name)?{??
  • ????????super(name);??
  • ????}??
  • ??????
  • ????//具體請求處理方法??
  • ????public?void?processRequest(PurchaseRequest?request)?{??
  • ????????if?(request.getAmount()?<?100000)?{??
  • ????????????System.out.println("副董事長"?+?this.name?+?"審批采購單:"?+?request.getNumber()?+?",金額:"?+?request.getAmount()?+?"元,采購目的:"?+?request.getPurpose()?+?"。");??//處理請求??
  • ????????}??
  • ????????else?{??
  • ????????????this.successor.processRequest(request);??//轉發請求??
  • ????????}?????
  • ????}??
  • }??
  • ??
  • //董事長類:具體處理者??
  • class?President?extends?Approver?{??
  • ????public?President(String?name)?{??
  • ????????super(name);??
  • ????}??
  • ??????
  • ????//具體請求處理方法??
  • ????public?void?processRequest(PurchaseRequest?request)?{??
  • ????????if?(request.getAmount()?<?500000)?{??
  • ????????????System.out.println("董事長"?+?this.name?+?"審批采購單:"?+?request.getNumber()?+?",金額:"?+?request.getAmount()?+?"元,采購目的:"?+?request.getPurpose()?+?"。");??//處理請求??
  • ????????}??
  • ????????else?{??
  • ????????????this.successor.processRequest(request);??//轉發請求??
  • ????????}??
  • ????}??
  • }??
  • ??
  • //董事會類:具體處理者??
  • class?Congress?extends?Approver?{??
  • ????public?Congress(String?name)?{??
  • ????????super(name);??
  • ????}??
  • ??????
  • ????//具體請求處理方法??
  • ????public?void?processRequest(PurchaseRequest?request)?{??
  • ????????System.out.println("召開董事會審批采購單:"?+?request.getNumber()?+?",金額:"?+?request.getAmount()?+?"元,采購目的:"?+?request.getPurpose()?+?"。");????????//處理請求??
  • ????}??????
  • }??
  • ????? 編寫如下客戶端測試代碼:? [java]?view plaincopy
  • class?Client?{??
  • ????public?static?void?main(String[]?args)?{??
  • ????????Approver?wjzhang,gyang,jguo,meeting;??
  • ????????wjzhang?=?new?Director("張無忌");??
  • ????????gyang?=?new?VicePresident("楊過");??
  • ????????jguo?=?new?President("郭靖");??
  • ????????meeting?=?new?Congress("董事會");??
  • ??????
  • ????????//創建職責鏈??
  • ????????wjzhang.setSuccessor(gyang);??
  • ????????gyang.setSuccessor(jguo);??
  • ????????jguo.setSuccessor(meeting);??
  • ??????????
  • ????????//創建采購單??
  • ????????PurchaseRequest?pr1?=?new?PurchaseRequest(45000,10001,"購買倚天劍");??
  • ????????wjzhang.processRequest(pr1);??
  • ??????????
  • ????????PurchaseRequest?pr2?=?new?PurchaseRequest(60000,10002,"購買《葵花寶典》");??
  • ????????wjzhang.processRequest(pr2);??
  • ??????
  • ????????PurchaseRequest?pr3?=?new?PurchaseRequest(160000,10003,"購買《金剛經》");??
  • ????????wjzhang.processRequest(pr3);??
  • ??
  • ????????PurchaseRequest?pr4?=?new?PurchaseRequest(800000,10004,"購買桃花島");??
  • ????????wjzhang.processRequest(pr4);??
  • ????}??
  • }???
  • ???????編譯并運行程序,輸出結果如下:

    主任張無忌審批采購單:10001,金額:45000.0元,采購目的:購買倚天劍。

    副董事長楊過審批采購單:10002,金額:60000.0元,采購目的:購買《葵花寶典》。

    董事長郭靖審批采購單:10003,金額:160000.0元,采購目的:購買《金剛經》。

    召開董事會審批采購單:10004,金額:800000.0元,采購目的:購買桃花島。

    ????? 如果需要在系統增加一個新的具體處理者,如增加一個經理(Manager)角色可以審批5萬元至8萬元(不包括8萬元)的采購單,需要編寫一個新的具體處理者類Manager,作為抽象處理者類Approver的子類,實現在Approver類中定義的抽象處理方法,如果采購金額大于等于8萬元,則將請求轉發給下家,代碼如下所示:

    [java]?view plaincopy
  • //經理類:具體處理者??
  • class?Manager?extends?Approver?{??
  • ????public?Manager(String?name)?{??
  • ????????super(name);??
  • ????}??
  • ??????
  • ????//具體請求處理方法??
  • ????public?void?processRequest(PurchaseRequest?request)?{??
  • ????????if?(request.getAmount()?<?80000)?{??
  • ????????????System.out.println("經理"?+?this.name?+?"審批采購單:"?+?request.getNumber()?+?",金額:"?+?request.getAmount()?+?"元,采購目的:"?+?request.getPurpose()?+?"。");??//處理請求??
  • ????????}??
  • ????????else?{??
  • ????????????this.successor.processRequest(request);??//轉發請求??
  • ????????}?????
  • ????}??
  • }??
  • ???????由于鏈的創建過程由客戶端負責,因此增加新的具體處理者類對原有類庫無任何影響,無須修改已有類的源代碼,符合“開閉原則”。

    ????? 在客戶端代碼中,如果要將新的具體請求處理者應用在系統中,需要創建新的具體處理者對象,然后將該對象加入職責鏈中。如在客戶端測試代碼中增加如下代碼:

    [java]?view plaincopy
  • Approver?rhuang;??
  • rhuang?=?new?Manager("黃蓉");??
  • ????????將建鏈代碼改為:

    [java]?view plaincopy
  • //創建職責鏈??
  • wjzhang.setSuccessor(rhuang);?//將“黃蓉”作為“張無忌”的下家??
  • rhuang.setSuccessor(gyang);?//將“楊過”作為“黃蓉”的下家??
  • gyang.setSuccessor(jguo);??
  • jguo.setSuccessor(meeting);??
  • ?????? 重新編譯并運行程序,輸出結果如下:

    主任張無忌審批采購單:10001,金額:45000.0元,采購目的:購買倚天劍。

    經理黃蓉審批采購單:10002,金額:60000.0元,采購目的:購買《葵花寶典》。

    董事長郭靖審批采購單:10003,金額:160000.0元,采購目的:購買《金剛經》。

    召開董事會審批采購單:10004,金額:800000.0元,采購目的:購買桃花島。

    ?

    ?

    思考

    ?????? 如果將審批流程由“主任-->副董事長-->董事長-->董事會”調整為“主任-->董事長-->董事會”,系統將做出哪些改動?預測修改之后客戶端代碼的輸出結果。


    16.4?純與不純的職責鏈模式

    ??????職責鏈模式可分為純的職責鏈模式和不純的職責鏈模式兩種:

    ?

    ?????? (1)?純的職責鏈模式

    ????? 一個純的職責鏈模式要求一個具體處理者對象只能在兩個行為中選擇一個:要么承擔全部責任,要么將責任推給下家不允許出現某一個具體處理者對象在承擔了一部分或全部責任后又將責任向下傳遞的情況。而且在純的職責鏈模式中,要求一個請求必須被某一個處理者對象所接收,不能出現某個請求未被任何一個處理者對象處理的情況。在前面的采購單審批實例中應用的是純的職責鏈模式。

    ?

    ?????? (2)不純的職責鏈模式

    ????? 在一個不純的職責鏈模式中允許某個請求被一個具體處理者部分處理后再向下傳遞,或者一個具體處理者處理完某請求后其后繼處理者可以繼續處理該請求,而且一個請求可以最終不被任何處理者對象所接收Java AWT 1.0中的事件處理模型應用的是不純的職責鏈模式,其基本原理如下:由于窗口組件(如按鈕、文本框等)一般都位于容器組件中,因此當事件發生在某一個組件上時,先通過組件對象的handleEvent()方法將事件傳遞給相應的事件處理方法,該事件處理方法將處理此事件,然后決定是否將該事件向上一級容器組件傳播;上級容器組件在接到事件之后可以繼續處理此事件并決定是否繼續向上級容器組件傳播,如此反復,直到事件到達頂層容器組件為止;如果一直傳到最頂層容器仍沒有處理方法,則該事件不予處理。每一級組件在接收到事件時,都可以處理此事件,而不論此事件是否在上一級已得到處理,還存在事件未被處理的情況顯然,這就是不純的職責鏈模式,早期的Java AWT事件模型(JDK 1.0及更早)中的這種事件處理機制又叫事件浮升(Event Bubbling)機制。從Java.1.1以后,JDK使用觀察者模式代替職責鏈模式來處理事件。目前,在JavaScript中仍然可以使用這種事件浮升機制來進行事件處理。

    ?

    16.5 職責鏈模式總結

    ??????職責鏈模式通過建立一條鏈來組織請求的處理者,請求將沿著鏈進行傳遞,請求發送者無須知道請求在何時、何處以及如何被處理,實現了請求發送者與處理者的解耦。在軟件開發中,如果遇到有多個對象可以處理同一請求時可以應用職責鏈模式,例如在Web應用開發中創建一個過濾器(Filter)來對請求數據進行過濾,在工作流系統中實現公文的分級審批等等,使用職責鏈模式可以較好地解決此類問題。

    ?

    ?????? 1.主要優點

    ????? 職責鏈模式的主要優點如下:

    ?????? (1)?職責鏈模式使得一個對象無須知道是其他哪一個對象處理其請求,對象僅需知道該請求會被處理即可,接收者和發送者都沒有對方的明確信息,且鏈中的對象不需要知道鏈的結構,由客戶端負責鏈的創建,降低了系統的耦合度。

    ?????? (2)?請求處理對象僅需維持一個指向其后繼者的引用,而不需要維持它對所有的候選處理者的引用,可簡化對象的相互連接。

    ?????? (3)?在給對象分派職責時,職責鏈可以給我們更多的靈活性,可以通過在運行時對該鏈進行動態的增加或修改來增加或改變處理一個請求的職責。

    ?????? (4)?在系統中增加一個新的具體請求處理者時無須修改原有系統的代碼,只需要在客戶端重新建鏈即可,從這一點來看是符合“開閉原則”的。

    ??????

    ????? ?2.主要缺點

    ????? 職責鏈模式的主要缺點如下:

    ?????? (1)?由于一個請求沒有明確的接收者,那么就不能保證它一定會被處理,該請求可能一直到鏈的末端都得不到處理;一個請求也可能因職責鏈沒有被正確配置而得不到處理。

    ?????? (2)?對于比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到一定影響,而且在進行代碼調試時不太方便。

    ?????? (3)?如果建鏈不當,可能會造成循環調用,將導致系統陷入死循環。

    ?

    ?????? 3.適用場景

    ????? 在以下情況下可以考慮使用職責鏈模式:

    ?????? (1)?有多個對象可以處理同一個請求,具體哪個對象處理該請求待運行時刻再確定,客戶端只需將請求提交到鏈上,而無須關心請求的處理對象是誰以及它是如何處理的。

    ?????? (2)?在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。

    ????? ??(3)?可動態指定一組對象處理請求,客戶端可以動態創建職責鏈來處理請求,還可以改變鏈中處理者之間的先后次序。?

    ?

    ?

    練習

    ?????? Sunny軟件公司的OA系統需要提供一個假條審批模塊:如果員工請假天數小于3天,主任可以審批該假條;如果員工請假天數大于等于3天,小于10天,經理可以審批;如果員工請假天數大于等于10天,小于30天,總經理可以審批;如果超過30天,總經理也不能審批,提示相應的拒絕信息。試用職責鏈模式設計該假條審批模塊。

    ?



    超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

    總結

    以上是生活随笔為你收集整理的请求的链式处理——职责链模式的全部內容,希望文章能夠幫你解決所遇到的問題。

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