0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

GoF設(shè)計(jì)模式之代理模式

元閏子的邀請(qǐng) ? 來源:元閏子的邀請(qǐng) ? 作者:元閏子的邀請(qǐng) ? 2022-10-17 09:45 ? 次閱讀

上一篇:【Go實(shí)現(xiàn)】實(shí)踐GoF的23種設(shè)計(jì)模式:訪問者模式

簡(jiǎn)單的分布式應(yīng)用系統(tǒng)(示例代碼工程):https://github.com/ruanrunxue/Practice-Design-Pattern--Go-Implementation

簡(jiǎn)介

GoF 對(duì)代理模式(Proxy Pattern)的定義如下:

Provide a surrogate or placeholder for another object to control access to it.

也即,代理模式為一個(gè)對(duì)象提供一種代理以控制對(duì)該對(duì)象的訪問

它是一個(gè)使用率非常高的設(shè)計(jì)模式,在現(xiàn)實(shí)生活中,也是很常見。比如,演唱會(huì)門票黃牛。假設(shè)你需要看一場(chǎng)演唱會(huì),但官網(wǎng)上門票已經(jīng)售罄,于是就當(dāng)天到現(xiàn)場(chǎng)通過黃牛高價(jià)買了一張。在這個(gè)例子中,黃牛就相當(dāng)于演唱會(huì)門票的代理,在正式渠道無法購(gòu)買門票的情況下,你通過代理完成了該目標(biāo)。

從演唱會(huì)門票的例子我們也能看出,使用代理模式的關(guān)鍵在于,當(dāng) Client 不方便直接訪問一個(gè)對(duì)象時(shí),提供一個(gè)代理對(duì)象控制該對(duì)象的訪問。Client 實(shí)際上訪問的是代理對(duì)象,代理對(duì)象會(huì)將 Client 的請(qǐng)求轉(zhuǎn)給本體對(duì)象去處理。

UML 結(jié)構(gòu)

5f53c40e-4d3e-11ed-a3b6-dac502259ad0.jpg

場(chǎng)景上下文

在簡(jiǎn)單的分布式應(yīng)用系統(tǒng)(示例代碼工程)中,db 模塊用來存儲(chǔ)服務(wù)注冊(cè)和監(jiān)控信息,它是一個(gè) key-value 數(shù)據(jù)庫。為了提升訪問數(shù)據(jù)庫的性能,我們決定為它新增一層緩存:

5f860194-4d3e-11ed-a3b6-dac502259ad0.jpg

另外,我們希望客戶端在使用數(shù)據(jù)庫時(shí),并不感知緩存的存在,這些,代理模式可以做到。

代碼實(shí)現(xiàn)

//demo/db/cache.go
packagedb

//關(guān)鍵點(diǎn)1:定義代理對(duì)象,實(shí)現(xiàn)被代理對(duì)象的接口
typeCacheProxystruct{
//關(guān)鍵點(diǎn)2:組合被代理對(duì)象,這里應(yīng)該是抽象接口,提升可擴(kuò)展性
dbDb
cachesync.Map//key為tableName,value為sync.Map[key:primaryId,value:interface{}]
hitint
missint
}

//關(guān)鍵點(diǎn)3:在具體接口實(shí)現(xiàn)上,嵌入代理本身的邏輯
func(c*CacheProxy)Query(tableNamestring,primaryKeyinterface{},resultinterface{})error{
cache,ok:=c.cache.Load(tableName)
ifok{
ifrecord,ok:=cache.(*sync.Map).Load(primaryKey);ok{
c.hit++
result=record
returnnil
}
}
c.miss++
iferr:=c.db.Query(tableName,primaryKey,result);err!=nil{
returnerr
}
cache.(*sync.Map).Store(primaryKey,result)
returnnil
}

func(c*CacheProxy)Insert(tableNamestring,primaryKeyinterface{},recordinterface{})error{
iferr:=c.db.Insert(tableName,primaryKey,record);err!=nil{
returnerr
}
cache,ok:=c.cache.Load(tableName)
if!ok{
returnnil
}
cache.(*sync.Map).Store(primaryKey,record)
returnnil
}

...

//關(guān)鍵點(diǎn)4:代理也可以有自己特有方法,提供一些輔助的功能
func(c*CacheProxy)Hit()int{
returnc.hit
}

func(c*CacheProxy)Miss()int{
returnc.miss
}

...

客戶端這樣使用:

//客戶端只看到抽象的Db接口
funcclient(dbDb){
table:=NewTable("region").
WithType(reflect.TypeOf(new(testRegion))).
WithTableIteratorFactory(NewRandomTableIteratorFactory())
db.CreateTable(table)
table.Insert(1,&testRegion{Id:1,Name:"region"})

result:=new(testRegion)
db.Query("region",1,result)
}

funcmain(){
//關(guān)鍵點(diǎn)5:在初始化階段,完成緩存的實(shí)例化,并依賴注入到客戶端
cache:=NewCacheProxy(&memoryDb{tables:sync.Map{}})
client(cache)
}

本例子中,Subject 是Db接口,Proxy 是CacheProxy對(duì)象,SubjectImpl 是memoryDb對(duì)象:

5fa4c21e-4d3e-11ed-a3b6-dac502259ad0.jpg

總結(jié)實(shí)現(xiàn)代理模式的幾個(gè)關(guān)鍵點(diǎn):

定義代理對(duì)象,實(shí)現(xiàn)被代理對(duì)象的接口。本例子中,前者是CacheProxy對(duì)象,后者是Db接口。

代理對(duì)象組合被代理對(duì)象,這里組合的應(yīng)該是抽象接口,讓代理的可擴(kuò)展性更高些。本例子中,CacheProxy對(duì)象組合了Db接口。

代理對(duì)象在具體接口實(shí)現(xiàn)上,嵌入代理本身的邏輯。本例子中,CacheProxy在Query、Insert等方法中,加入了緩存sync.Map的讀寫邏輯。

代理對(duì)象也可以有自己特有方法,提供一些輔助的功能。本例子中,CacheProxy新增了Hit、Miss等方法用于統(tǒng)計(jì)緩存的命中率。

最后,在初始化階段,完成代理的實(shí)例化,并依賴注入到客戶端。這要求,客戶端依賴抽象接口,而不是具體實(shí)現(xiàn),否則代理就不透明了。

擴(kuò)展

Go 標(biāo)準(zhǔn)庫中的反向代理

代理模式最典型的應(yīng)用場(chǎng)景是遠(yuǎn)程代理,其中,反向代理又是最常用的一種。

以 Web 應(yīng)用為例,反向代理位于 Web 服務(wù)器前面,將客戶端(例如 Web 瀏覽器)請(qǐng)求轉(zhuǎn)發(fā)后端的 Web 服務(wù)器。反向代理通常用于幫助提高安全性、性能和可靠性,比如負(fù)載均衡、SSL 安全鏈接。

5fcd9482-4d3e-11ed-a3b6-dac502259ad0.jpg

Go 標(biāo)準(zhǔn)庫的 net 包也提供了反向代理,ReverseProxy,位于net/http/httputil/reverseproxy.go下,實(shí)現(xiàn)http.Handler接口。http.Handler提供了處理 Http 請(qǐng)求的能力,也即相當(dāng)于 Http 服務(wù)器。那么,對(duì)應(yīng)到 UML 結(jié)構(gòu)圖中,http.Handler就是 Subject,ReverseProxy就是 Proxy:

5ffc9af2-4d3e-11ed-a3b6-dac502259ad0.jpg

下面列出ReverseProxy的一些核心代碼:

//net/http/httputil/reverseproxy.go
packagehttputil

typeReverseProxystruct{
//修改前端請(qǐng)求,然后通過Transport將修改后的請(qǐng)求轉(zhuǎn)發(fā)給后端
Directorfunc(*http.Request)
//可理解為Subject,通過Transport來調(diào)用被代理對(duì)象的ServeHTTP方法處理請(qǐng)求
Transporthttp.RoundTripper
//修改后端響應(yīng),并將修改后的響應(yīng)返回給前端
ModifyResponsefunc(*http.Response)error
//錯(cuò)誤處理
ErrorHandlerfunc(http.ResponseWriter,*http.Request,error)
...
}

func(p*ReverseProxy)ServeHTTP(rwhttp.ResponseWriter,req*http.Request){
//初始化transport
transport:=p.Transport
iftransport==nil{
transport=http.DefaultTransport
}
...
//修改前端請(qǐng)求
p.Director(outreq)
...
//將請(qǐng)求轉(zhuǎn)發(fā)給后端
res,err:=transport.RoundTrip(outreq)
...
//修改后端響應(yīng)
if!p.modifyResponse(rw,res,outreq){
return
}
...
//給前端返回響應(yīng)
err=p.copyResponse(rw,res.Body,p.flushInterval(res))
...
}

ReverseProxy就是典型的代理模式實(shí)現(xiàn),其中,遠(yuǎn)程代理無法直接引用后端的對(duì)象引用,因此這里通過引入Transport來遠(yuǎn)程訪問后端服務(wù),可以將Transport理解為 Subject。

可以這么使用ReverseProxy:

funcproxy(c*gin.Context){
remote,err:=url.Parse("https://yrunz.com")
iferr!=nil{
panic(err)
}

proxy:=httputil.NewSingleHostReverseProxy(remote)
proxy.Director=func(req*http.Request){
req.Header=c.Request.Header
req.Host=remote.Host
req.URL.Scheme=remote.Scheme
req.URL.Host=remote.Host
req.URL.Path=c.Param("proxyPath")
}

proxy.ServeHTTP(c.Writer,c.Request)
}

funcmain(){
r:=gin.Default()
r.Any("/*proxyPath",proxy)
r.Run(":8080")
}

典型應(yīng)用場(chǎng)景

遠(yuǎn)程代理(remote proxy),遠(yuǎn)程代理適用于提供服務(wù)的對(duì)象處在遠(yuǎn)程的機(jī)器上,通過普通的函數(shù)調(diào)用無法使用服務(wù),需要經(jīng)過遠(yuǎn)程代理來完成。因?yàn)椴⒉荒苤苯釉L問本體對(duì)象,所有遠(yuǎn)程代理對(duì)象通常不會(huì)直接持有本體對(duì)象的引用,而是持有遠(yuǎn)端機(jī)器的地址,通過網(wǎng)絡(luò)協(xié)議去訪問本體對(duì)象。

虛擬代理(virtual proxy),在程序設(shè)計(jì)中常常會(huì)有一些重量級(jí)的服務(wù)對(duì)象,如果一直持有該對(duì)象實(shí)例會(huì)非常消耗系統(tǒng)資源,這時(shí)可以通過虛擬代理來對(duì)該對(duì)象進(jìn)行延遲初始化。

保護(hù)代理(protection proxy),保護(hù)代理用于控制對(duì)本體對(duì)象的訪問,常用于需要給 Client 的訪問加上權(quán)限驗(yàn)證的場(chǎng)景。

緩存代理(cache proxy),緩存代理主要在 Client 與本體對(duì)象之間加上一層緩存,用于加速本體對(duì)象的訪問,常見于連接數(shù)據(jù)庫的場(chǎng)景。

智能引用(smart reference),智能引用為本體對(duì)象的訪問提供了額外的動(dòng)作,常見的實(shí)現(xiàn)為 C++ 中的智能指針,為對(duì)象的訪問提供了計(jì)數(shù)功能,當(dāng)訪問對(duì)象的計(jì)數(shù)為 0 時(shí)銷毀該對(duì)象。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

可以在客戶端不感知的情況下,控制訪問對(duì)象,比如遠(yuǎn)程訪問、增加緩存、安全等。

符合開閉原則,可以在不修改客戶端和被代理對(duì)象的前提下,增加新的代理;也可以在不修改客戶端和代理的前提下,更換被代理對(duì)象。

缺點(diǎn)

作為遠(yuǎn)程代理時(shí),因?yàn)槎嗔艘淮无D(zhuǎn)發(fā),會(huì)影響請(qǐng)求的時(shí)延。

與其他模式的關(guān)聯(lián)

從結(jié)構(gòu)上看,裝飾模式和 代理模式 具有很高的相似性,但是兩種所強(qiáng)調(diào)的點(diǎn)不一樣。前者強(qiáng)調(diào)的是為本體對(duì)象添加新的功能,后者強(qiáng)調(diào)的是對(duì)本體對(duì)象的訪問控制。

文章配圖

可以在用Keynote畫出手繪風(fēng)格的配圖中找到文章的繪圖方法。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68231
  • 設(shè)計(jì)模式
    +關(guān)注

    關(guān)注

    0

    文章

    53

    瀏覽量

    8620
  • Client
    +關(guān)注

    關(guān)注

    0

    文章

    10

    瀏覽量

    8749
  • 代理模式
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    1754

原文標(biāo)題:【Go實(shí)現(xiàn)】實(shí)踐GoF的23種設(shè)計(jì)模式:代理模式

文章出處:【微信號(hào):yuanrunzi,微信公眾號(hào):元閏子的邀請(qǐng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    適配器模式代理模式的區(qū)別

      代理模式  組成:  抽象角色:通過接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法?! ?b class='flag-5'>代理角色:實(shí)現(xiàn)抽象角色,是真實(shí)角色的代理,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法,并可以附加自己的
    發(fā)表于 10-22 15:17

    23種基本的設(shè)計(jì)模式總結(jié)

    一樣。?提到設(shè)計(jì)模式,不得不感謝GoF(***,四人組),他們1995年出版的《設(shè)計(jì)模式》一書,第一次將設(shè)計(jì)模式提升到理論高度,并將之規(guī)范化。書中一共總結(jié)了23種基本的設(shè)計(jì)
    發(fā)表于 03-01 06:08

    Command模式與動(dòng)態(tài)語言

    Gof的設(shè)計(jì)模式中,有一個(gè)模式引起的爭(zhēng)議比較大,有很多人甚至認(rèn)為這個(gè)模式應(yīng)該排除在OO模式之外,原因在于它不具有OO的特性
    發(fā)表于 06-22 10:20 ?981次閱讀
    Command<b class='flag-5'>模式</b>與動(dòng)態(tài)語言

    Modbus ASCII 模式通訊程序

    C語言編寫的臺(tái)達(dá)變頻器Modbus ASCII 模式通訊程序
    發(fā)表于 12-02 10:25 ?6次下載

    適配器模式、裝飾器模式、代理模式的區(qū)別

    適配器模式、裝飾器模式、代理模式都屬于設(shè)計(jì)模式中的結(jié)構(gòu)型模式,結(jié)構(gòu)型設(shè)計(jì)
    發(fā)表于 10-18 15:53 ?1.7w次閱讀
    適配器<b class='flag-5'>模式</b>、裝飾器<b class='flag-5'>模式</b>、<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的區(qū)別

    適配器模式代理模式的區(qū)別

    適配器模式:適配器模式有時(shí)候也稱包裝樣式或者包裝。將一個(gè)類的接口轉(zhuǎn)接成用戶所期待的。代理模式:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。
    發(fā)表于 01-12 11:56 ?5262次閱讀
    適配器<b class='flag-5'>模式</b>和<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的區(qū)別

    C語言設(shè)計(jì)模式的程序資料合集

    本文檔的主要內(nèi)容詳細(xì)介紹的是C語言設(shè)計(jì)模式的程序資料合集包括了:C語言設(shè)計(jì)模式_繼承-多態(tài)-封裝,C語言設(shè)計(jì)模式_單件
    發(fā)表于 11-16 08:00 ?4次下載

    嵌入式軟件設(shè)計(jì)設(shè)計(jì)模式

    文章目錄前言1.設(shè)計(jì)模式適配器模式2.設(shè)計(jì)模式單例模式3.設(shè)計(jì)
    發(fā)表于 10-21 11:07 ?9次下載
    嵌入式軟件設(shè)計(jì)<b class='flag-5'>之</b>設(shè)計(jì)<b class='flag-5'>模式</b>

    GoF設(shè)計(jì)模式觀察者模式

    現(xiàn)在有 2 個(gè)服務(wù),Service A 和 Service B,通過 REST 接口通信;Service A 在某個(gè)業(yè)務(wù)場(chǎng)景下調(diào)用 Service B 的接口完成一個(gè)計(jì)算密集型任務(wù),假設(shè)接口為 http://service_b/api/v1/domain;該任務(wù)運(yùn)行時(shí)間很長(zhǎng),但 Service A 不想一直阻塞在接口調(diào)用上。為了滿足 Service A 的要求,通常有 2 種方案:
    的頭像 發(fā)表于 07-25 11:32 ?989次閱讀

    GoF設(shè)計(jì)模式訪問者模式

    訪問者模式的目的是,解耦數(shù)據(jù)結(jié)構(gòu)和算法,使得系統(tǒng)能夠在不改變現(xiàn)有代碼結(jié)構(gòu)的基礎(chǔ)上,為對(duì)象新增一種新的操作。
    的頭像 發(fā)表于 10-08 11:05 ?658次閱讀

    實(shí)踐GoF的23種設(shè)計(jì)模式:命令模式簡(jiǎn)介

    因此,我們需要對(duì)請(qǐng)求進(jìn)行抽象,將上下文信息封裝到請(qǐng)求對(duì)象里,這其實(shí)就是命令模式,而該請(qǐng)求對(duì)象就是 Command。
    的頭像 發(fā)表于 01-13 16:36 ?717次閱讀

    設(shè)計(jì)模式結(jié)構(gòu)性:代理模式

    代理模式(Proxy Pattern)中,一個(gè)類代表另一個(gè)類的功能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。
    的頭像 發(fā)表于 06-09 15:27 ?806次閱讀
    設(shè)計(jì)<b class='flag-5'>模式</b>結(jié)構(gòu)性:<b class='flag-5'>代理</b><b class='flag-5'>模式</b>

    設(shè)計(jì)模式代理模式的使用場(chǎng)景

    設(shè)計(jì)模式在我看來更像是一種設(shè)計(jì)思維或設(shè)計(jì)思想,它就像《孫子兵法》一樣,為你的項(xiàng)目工程提供方向,讓你的項(xiàng)目工程更加健壯、靈活,延續(xù)生命力。本文即將分享的是設(shè)計(jì)模式的其中一種:代理模式。
    的頭像 發(fā)表于 10-08 14:34 ?928次閱讀
    設(shè)計(jì)<b class='flag-5'>模式</b>中<b class='flag-5'>代理</b><b class='flag-5'>模式</b>的使用場(chǎng)景

    實(shí)踐GoF的23種設(shè)計(jì)模式:備忘錄模式

    相對(duì)于代理模式、工廠模式等設(shè)計(jì)模式,備忘錄模式(Memento)在我們?nèi)粘i_發(fā)中出鏡率并不高,除了應(yīng)用場(chǎng)景的限制之外,另一個(gè)原因,可能是備忘
    的頭像 發(fā)表于 11-25 09:05 ?506次閱讀
    實(shí)踐<b class='flag-5'>GoF</b>的23種設(shè)計(jì)<b class='flag-5'>模式</b>:備忘錄<b class='flag-5'>模式</b>

    實(shí)踐GoF的23種設(shè)計(jì)模式:解釋器模式

    解釋器模式(Interpreter Pattern)應(yīng)該是 GoF 的 23 種設(shè)計(jì)模式中使用頻率最少的一種了,它的應(yīng)用場(chǎng)景較為局限。
    的頭像 發(fā)表于 04-01 11:01 ?606次閱讀
    實(shí)踐<b class='flag-5'>GoF</b>的23種設(shè)計(jì)<b class='flag-5'>模式</b>:解釋器<b class='flag-5'>模式</b>