<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go設(shè)計(jì)模式之責(zé)任鏈模式

          共 13649字,需瀏覽 28分鐘

           ·

          2023-08-29 22:12

          其實(shí)很多人不知道,責(zé)任鏈模式是我們工作中經(jīng)常遇到的模式,特別是web后端工程師,我們工作中每時(shí)每刻都在用:因?yàn)槭忻嫔洗蟛糠值膚eb框架的過濾器基本都是基于這個(gè)設(shè)計(jì)模式為基本模式搭建的。

          1.模式介紹

          我們先來看一下責(zé)任鏈模式(Chain Of Responsibility Design Pattern )的英文介紹: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.

          翻譯成中文就是:將請(qǐng)求的發(fā)送和接收解耦,讓多個(gè)接收對(duì)象都有機(jī)會(huì)處理這個(gè)請(qǐng)求。將這些接收對(duì)象串成一條鏈,并沿著這條鏈傳遞這個(gè)請(qǐng)求,直到鏈上的某個(gè)接收對(duì)象能夠處理它為止。

          這么說比較抽象,用更加容易理解的話來進(jìn)一步解讀一下。在責(zé)任鏈模式中,一個(gè)請(qǐng)求過來,會(huì)有多個(gè)處理器(也就是剛剛定義中說的“接收對(duì)象”)依次處理同一個(gè)請(qǐng)求。即請(qǐng)求先經(jīng)過 A 處理器處理,然后再把請(qǐng)求傳遞給 B 處理器,B 處理器處理完后再傳遞給 C 處理器,以此類推,形成一個(gè)鏈條。鏈條上的每個(gè)處理器各自承擔(dān)各自的處理職責(zé),所以叫作責(zé)任鏈模式。

          6b64d200bc942628d249652bb5615da2.webp

          (請(qǐng)雙擊圖片查看)

          2.模式demo

          2.1 UML

          責(zé)任鏈模式(Chain Of Responsibility Design Pattern )的整體結(jié)構(gòu)如下:

          1aa34b7d4f8744e1269cd10c843f38a3.webp

          (請(qǐng)雙 擊圖片查看 )

          2.2 標(biāo)準(zhǔn)demo

          我們依據(jù)標(biāo)準(zhǔn)的UML圖,寫出一個(gè)具體的例子(對(duì)應(yīng)UML圖):

          838b42013b205a1dfb088e6099a158bf.webp

          (請(qǐng)雙 擊圖片查看 )

          首先定義一個(gè)接口 IHandler

              
          1. type IHandler interface {

          2. SetNext ( handler IHandler )

          3. Handle ( score int )

          4. }

          然后分別構(gòu)建三個(gè)不同的實(shí)現(xiàn): ConcreteHandler1

              
          1. type ConcreteHandler1 struct {

          2. Next IHandler

          3. }

          4. func ( c * ConcreteHandler1 ) Handle ( score int ) {

          5. if score < 0 {

          6. fmt . Println ( "ConcreteHandler1 處理" )

          7. return

          8. }

          9. if c . Next != nil {

          10. c . Next . Handle ( score )

          11. }

          12. return

          13. }

          14. func ( c * ConcreteHandler1 ) SetNext ( handler IHandler ) {

          15. c . Next = handler

          16. }

          ConcreteHandler2

              
          1. type ConcreteHandler2 struct {

          2. Next IHandler

          3. }

          4. func ( c * ConcreteHandler2 ) Handle ( score int ) {

          5. if score > 0 {

          6. fmt . Println ( "ConcreteHandler2 處理" )

          7. return

          8. }

          9. if c . Next != nil {

          10. c . Next . Handle ( score )

          11. }

          12. return

          13. }

          14. func ( c * ConcreteHandler2 ) SetNext ( handler IHandler ) {

          15. c . Next = handler

          16. }

          ConcreteHandler3

              
          1. type ConcreteHandler3 struct {

          2. Next IHandler

          3. }

          4. func ( c * ConcreteHandler3 ) Handle ( score int ) {

          5. if score == 0 {

          6. fmt . Println ( "ConcreteHandler3 處理" )

          7. return

          8. }

          9. if c . Next != nil {

          10. c . Next . Handle ( score )

          11. }

          12. return

          13. }

          14. func ( c * ConcreteHandler3 ) SetNext ( handler IHandler ) {

          15. c . Next = handler

          16. }

          最后是 main函數(shù):

              
          1. func main () {

          2. handler1 := & ConcreteHandler1 {}

          3. handler2 := & ConcreteHandler2 {}

          4. handler3 := & ConcreteHandler3 {}

          5. handler1 . SetNext ( handler2 )

          6. handler2 . SetNext ( handler3 )

          7. handler1 . Handle ( 10 )

          8. }

          打印結(jié)果為:

              
          1. ConcreteHandler2 處理

          2.3 改進(jìn)版demo

          通過以上標(biāo)準(zhǔn)例子不難發(fā)現(xiàn): main函數(shù)承接了很多client自身之外的“額外工作”:構(gòu)建和拼接組裝責(zé)任鏈,這不利于后續(xù)client端的使用和擴(kuò)展:一不小心可能責(zé)任鏈拼就接錯(cuò)了或者拼接少節(jié)點(diǎn)了。我們可以對(duì)UML做一個(gè)改進(jìn):增加一個(gè)節(jié)點(diǎn)管理模塊。改進(jìn)圖如下:

          80b6264e45481193676a5d4541c572e0.webp

          (請(qǐng)雙 擊圖片查看 )

          對(duì)比上文的uml圖,新增加了一個(gè) ChainHandler結(jié)構(gòu)體用來管理拼接的 Handler,client端無需了解 Handler的業(yè)務(wù), Handler的組合可以使用鏈表,也可以使用數(shù)組(當(dāng)前用了數(shù)組)。具體實(shí)現(xiàn)如下:先定義 Handler接口:

              
          1. type Handler interface {

          2. Handle ( score int )

          3. }

          然后分別實(shí)現(xiàn) Handler接口的三個(gè)結(jié)構(gòu)體: ConcreteHandlerOne

              
          1. type ConcreteHandlerOne struct {

          2. Handler

          3. }

          4. func ( c * ConcreteHandlerOne ) Handle ( score int ) {

          5. if score < 0 {

          6. fmt . Println ( "ConcreteHandler1 處理" )

          7. return

          8. }

          9. }

          ConcreteHandlerTwo

              
          1. type ConcreteHandlerTwo struct {

          2. Handler

          3. }

          4. func ( c * ConcreteHandlerTwo ) Handle ( score int ) {

          5. if score > 0 {

          6. fmt . Println ( "ConcreteHandler2 處理" )

          7. return

          8. }

          9. }

          ConcreteHandlerThree

              
          1. type ConcreteHandlerThree struct {

          2. Handler

          3. }

          4. func ( c * ConcreteHandlerThree ) Handle ( score int ) {

          5. if score == 0 {

          6. fmt . Println ( "ConcreteHandler3 處理" )

          7. return

          8. }

          9. }

          main函數(shù)調(diào)用(client調(diào)用):

              
          1. func main () {

          2. chain := & ChainHandler {}

          3. chain . AddHandler (& ConcreteHandlerOne {})

          4. chain . AddHandler (& ConcreteHandlerTwo {})

          5. chain . AddHandler (& ConcreteHandlerThree {})

          6. chain . Handle ( 10 )

          7. }

          最終的實(shí)現(xiàn)結(jié)構(gòu)圖:

          9ef241a782994f749a40d0596d029322.webp

          (請(qǐng)雙 擊圖片查看 )

          日常工作中出現(xiàn)的責(zé)任鏈模式(Chain Of Responsibility Design Pattern )一般都是以上這種包含 Hanlder管理的模式。

          3. 源碼解析

          在日常框架和語言基礎(chǔ)庫中,經(jīng)常能夠看到很多場(chǎng)景使用了責(zé)任鏈模式。

          3.1 beego過濾器

          可以對(duì)比改進(jìn)版demo的uml圖,beego的過濾器就是按照這種模式來設(shè)計(jì)的(當(dāng)前參照的beego版本是2.0)。

          d710f7d7ddba47eed0c5e09cc061e70d.webp

          (請(qǐng)雙 擊圖片查看 )

          3.1.1 client端

          調(diào)用端首先是過濾器的注冊(cè):

              
          1. web . InsertFilter ( "/v2/api/*" , web . BeforeRouter , auth . AuthAPIFilter )

          然后在 github.com/beego/beego/v2@v2.0.3/server/web/router.go的 ControllerRegister結(jié)構(gòu)體的 serveHttp函數(shù)中

              
          1. if len ( p . filters [ BeforeRouter ]) > 0 && p . execFilter ( ctx , urlPath , BeforeRouter ) {

          2. goto Admin

          3. }

          以上 p.execFilter(ctx,urlPath,BeforeRouter)處,啟動(dòng)調(diào)用。

          3.1.2 Handler接口

          Handler接口很簡(jiǎn)單

              
          1. // HandleFunc define how to process the request

          2. type HandleFunc func ( ctx * beecontext . Context )

          3. ...

          4. type FilterFunc = HandleFunc

          3.1.3 Handler接口實(shí)現(xiàn)

          接口的實(shí)現(xiàn)擴(kuò)展比較靈活,直接把用戶定義的函數(shù)作為接口的實(shí)現(xiàn)。與client端中的過濾器注冊(cè)聯(lián)動(dòng)。

              
          1. // 過濾器注冊(cè)

          2. web . InsertFilter ( "/v2/api/*" , web . BeforeRouter , auth . AuthAPIFilter )

          3. // 自定義過濾器

          4. var AuthAPIFilter = func ( ctx * context . Context ) {

          5. isAccess := validateAccess ( ctx )

          6. if ! isAccess {

          7. res , _ := json . Marshal ( r )

          8. ctx . WriteString ( string ( res ))

          9. // ctx.Redirect(401, "/401")

          10. }

          11. }

          3.1.4 Handler管理

          Handler的管理模塊是在 github.com/beego/beego/v2@v2.0.3/server/web/router.go的中的 FilterRouter和 ControllerRegister兩個(gè)結(jié)構(gòu)體中

              
          1. // ControllerRegister containers registered router rules, controller handlers and filters.

          2. type ControllerRegister struct {

          3. routers map [ string ]* Tree

          4. enablePolicy bool

          5. enableFilter bool

          6. policies map [ string ]* Tree

          7. filters [ FinishRouter + 1 ][]* FilterRouter

          8. pool sync . Pool

          9. // the filter created by FilterChain

          10. chainRoot * FilterRouter

          11. // keep registered chain and build it when serve http

          12. filterChains [] filterChainConfig

          13. cfg * Config

          14. }

          15. type FilterRouter struct {

          16. filterFunc FilterFunc

          17. next * FilterRouter

          18. tree * Tree

          19. pattern string

          20. returnOnOutput bool

          21. resetParams bool

          22. }

          FilterRouter是一個(gè)鏈表,包含用戶自定義的過濾函數(shù); ControllerRegister對(duì) FilterRouter進(jìn)行管理。

          3.2 Go源碼http.handler

          我們?cè)谑褂肎o構(gòu)建http web服務(wù)器的時(shí)候,使用的http.Handler就是使用的責(zé)任鏈模式。

              
          1. package main

          2. import (

          3. "net/http"

          4. )

          5. func main () {

          6. s := http . NewServeMux ()

          7. s . HandleFunc ( "/" , func ( writer http . ResponseWriter , request * http . Request ) {

          8. // todo ....

          9. return

          10. })

          11. http . ListenAndServe ( ":80" , s )

          12. }

          以 2.3UML為標(biāo)準(zhǔn),整體的對(duì)照結(jié)構(gòu)圖如下:

          912cde37b9ba714fbb98d00f51b5fe3f.webp

          (請(qǐng)雙 擊圖片查看 )

          3.2.1 client端

          整個(gè)模式的啟動(dòng)是隨著http server啟動(dòng)后,接受到請(qǐng)求后的處理開始的。在 net/http/server.go的 serve函數(shù)中

              
          1. func ( c * conn ) serve ( ctx context . Context ) {

          2. ...

          3. // HTTP cannot have multiple simultaneous active requests.[*]

          4. // Until the server replies to this request, it can't read another,

          5. // so we might as well run the handler in this goroutine.

          6. // [*] Not strictly true: HTTP pipelining. We could let them all process

          7. // in parallel even if their responses need to be serialized.

          8. // But we're not going to implement HTTP pipelining because it

          9. // was never deployed in the wild and the answer is HTTP/2.

          10. serverHandler { c . server }. ServeHTTP ( w , w . req )

          11. ...

          12. }

          可以看到http server的原理很簡(jiǎn)單,就是for 死循環(huán)等待接收,然后一個(gè)請(qǐng)求過來,就對(duì)應(yīng)的生成一個(gè)單獨(dú)的協(xié)程 goroutine去處理。

          3.2.2 Handler接口

          Go源碼中對(duì)責(zé)任鏈模式的實(shí)現(xiàn)非常標(biāo)準(zhǔn),Handler接口與設(shè)計(jì)模式中的Handler接口同名,在 net/http/server.go中:

              
          1. type Handler interface {

          2. ServeHTTP ( ResponseWriter , * Request )

          3. }

          為了擴(kuò)展方便,在使用過程中并非直接使用,而是中間又加了一層抽象層(相當(dāng)于Java中的抽象類了,Go中沒有抽象類)

              
          1. // The HandlerFunc type is an adapter to allow the use of

          2. // ordinary functions as HTTP handlers. If f is a function

          3. // with the appropriate signature, HandlerFunc(f) is a

          4. // Handler that calls f.

          5. type HandlerFunc func ( ResponseWriter , * Request )

          6. // ServeHTTP calls f(w, r).

          7. func ( f HandlerFunc ) ServeHTTP ( w ResponseWriter , r * Request ) {

          8. f ( w , r )

          9. }

          3.2.3 Handler接口實(shí)現(xiàn)

          與上文中提到的Beego的過濾器類似,Go的Handler設(shè)計(jì)的也非常容易擴(kuò)展,用戶自定義的請(qǐng)求處理函數(shù)Handler都會(huì)變成 Handler的子類。

              
          1. func main () {

          2. s := http . NewServeMux ()

          3. s . HandleFunc ( "/" , func ( writer http . ResponseWriter , request * http . Request ) {

          4. // todo ....

          5. return

          6. })

          7. http . ListenAndServe ( ":80" , s )

          8. }

          9. // HandleFunc registers the handler function for the given pattern.

          10. func ( mux * ServeMux ) HandleFunc ( pattern string , handler func ( ResponseWriter , * Request )) {

          11. if handler == nil {

          12. panic ( "http: nil handler" )

          13. }

          14. // 強(qiáng)制類型轉(zhuǎn)換,轉(zhuǎn)成了實(shí)現(xiàn)了Hanlder的“抽象類”HandlerFunc

          15. mux . Handle ( pattern , HandlerFunc ( handler ))

          16. }

          注意看上文的 HandleFunc中的 mux.Handle(pattern,HandlerFunc(handler))這一行,將用戶自定義的處理函數(shù)強(qiáng)制轉(zhuǎn)換成了上文3.2.2中的 Handler的"抽象類" HandlerFunc類型,進(jìn)而實(shí)現(xiàn)了繼承。

          3.2.4 Handler接口的管理類ChainHandler

          Go中對(duì)Handler的管理類是在 net/http/server.go文件的 ServeMux結(jié)構(gòu)體和 muxEntry結(jié)構(gòu)體中:

              
          1. type ServeMux struct {

          2. mu sync . RWMutex

          3. m map [ string ] muxEntry

          4. es [] muxEntry // slice of entries sorted from longest to shortest.

          5. hosts bool // whether any patterns contain hostnames

          6. }

          7. type muxEntry struct {

          8. h Handler

          9. pattern string

          10. }

          其中,用戶自定以的處理函數(shù)都被封裝到了 muxEntry結(jié)構(gòu)體的 Handler中,一個(gè)自定義的函數(shù)對(duì)應(yīng)一個(gè) muxEntry, ServeMux使用hashmap對(duì) muxEntry集合進(jìn)行管理(上文的beego中是使用的鏈表,上文demo中使用了數(shù)組)。當(dāng)web server接收到請(qǐng)求的時(shí)候, ServeMux會(huì)根據(jù)hashmap找到相應(yīng)的handler然后處理。

              
          1. func ( mux * ServeMux ) ServeHTTP ( w ResponseWriter , r * Request ) {

          2. if r . RequestURI == "*" {

          3. if r . ProtoAtLeast ( 1 , 1 ) {

          4. w . Header (). Set ( "Connection" , "close" )

          5. }

          6. w . WriteHeader ( StatusBadRequest )

          7. return

          8. }

          9. // *******尋找handler*******

          10. h , _ := mux . Handler ( r )

          11. h . ServeHTTP ( w , r )

          12. }

          13. func ( mux * ServeMux ) Handler ( r * Request ) ( h Handler , pattern string ) {

          14. ...

          15. if path != r . URL . Path {

          16. _ , pattern = mux . handler ( host , path )

          17. u := & url . URL { Path : path , RawQuery : r . URL . RawQuery }

          18. return RedirectHandler ( u . String (), StatusMovedPermanently ), pattern

          19. }

          20. // *******尋找handler*******

          21. return mux . handler ( host , r . URL . Path )

          22. }

          23. func ( mux * ServeMux ) handler ( host , path string ) ( h Handler , pattern string ) {

          24. mux . mu . RLock ()

          25. defer mux . mu . RUnlock ()

          26. // Host-specific pattern takes precedence over generic ones

          27. if mux . hosts {

          28. // *******尋找handler*******

          29. h , pattern = mux . match ( host + path )

          30. }

          31. if h == nil {

          32. // *******尋找handler*******

          33. h , pattern = mux . match ( path )

          34. }

          35. if h == nil {

          36. h , pattern = NotFoundHandler (), ""

          37. }

          38. return

          39. }

          40. func ( mux * ServeMux ) match ( path string ) ( h Handler , pattern string ) {

          41. // ********通過hashmap找到相關(guān)handler*********

          42. v , ok := mux . m [ path ]

          43. if ok {

          44. return v . h , v . pattern

          45. }

          46. for _ , e := range mux . es {

          47. if strings . HasPrefix ( path , e . pattern ) {

          48. return e . h , e . pattern

          49. }

          50. }

          51. return nil , ""

          52. }

          在程序運(yùn)行過程中,用戶注冊(cè)自定義的函數(shù)被轉(zhuǎn)化成了 Handler,然后 Handler又結(jié)合用戶自定義的 URL地址被 ServeMux以 URL為Key、 Handler為Value做成hashmap管理起來;等到請(qǐng)求來的時(shí)候, ServeMux就根據(jù)用戶請(qǐng)求的 URL地址,從hashmap中找到具體的 Hanlder來處理請(qǐng)求。

          4. 總結(jié)

          責(zé)任鏈模式的基本思想就是要處理的請(qǐng)求(通常會(huì)是結(jié)構(gòu)體,然后作為函數(shù)參數(shù));依次經(jīng)過多個(gè)處理對(duì)象處理,這些處理函數(shù)可以動(dòng)態(tài)的添加和刪除,具備很高的靈活性和擴(kuò)展性,通常會(huì)對(duì)這些處理函數(shù)做統(tǒng)一處理,存儲(chǔ)方式一般是通過鏈表、數(shù)組、hash map等存儲(chǔ)結(jié)構(gòu)。

          責(zé)任鏈模式的應(yīng)用非常廣泛:

          1. 業(yè)務(wù)場(chǎng)景:作為敏感詞(涉黃、政治、反動(dòng)等此)過濾的設(shè)計(jì)結(jié)構(gòu)

          2. 技術(shù)框架:路由、router過濾器、日志log框架等等



          推薦閱讀


          福利
          我為大家整理了一份 從入門到進(jìn)階的Go學(xué)習(xí)資料禮包 ,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。 關(guān)注公眾號(hào) 「polarisxu」,回復(fù)  ebook  獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人做天天摸夜夜添成人 | 日韩人妻无码一区二区三区99 | 影音先锋在线资源AV | 又色网站免费看 | 亚洲a免费视频 |