衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

GoF設計模式之代理模式

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

上一篇:【Go實現】實踐GoF的23種設計模式:訪問者模式

簡單的分布式應用系統(示例代碼工程):https://github.com/ruanrunxue/Practice-Design-Pattern--Go-Implementation

簡介

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

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

也即,代理模式為一個對象提供一種代理以控制對該對象的訪問

它是一個使用率非常高的設計模式,在現實生活中,也是很常見。比如,演唱會門票黃牛。假設你需要看一場演唱會,但官網上門票已經售罄,于是就當天到現場通過黃牛高價買了一張。在這個例子中,黃牛就相當于演唱會門票的代理,在正式渠道無法購買門票的情況下,你通過代理完成了該目標。

從演唱會門票的例子我們也能看出,使用代理模式的關鍵在于,當 Client 不方便直接訪問一個對象時,提供一個代理對象控制該對象的訪問。Client 實際上訪問的是代理對象,代理對象會將 Client 的請求轉給本體對象去處理。

UML 結構

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

場景上下文

在簡單的分布式應用系統(示例代碼工程)中,db 模塊用來存儲服務注冊和監控信息,它是一個 key-value 數據庫。為了提升訪問數據庫的性能,我們決定為它新增一層緩存:

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

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

代碼實現

//demo/db/cache.go
packagedb

//關鍵點1:定義代理對象,實現被代理對象的接口
typeCacheProxystruct{
//關鍵點2:組合被代理對象,這里應該是抽象接口,提升可擴展性
dbDb
cachesync.Map//key為tableName,value為sync.Map[key:primaryId,value:interface{}]
hitint
missint
}

//關鍵點3:在具體接口實現上,嵌入代理本身的邏輯
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
}

...

//關鍵點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(){
//關鍵點5:在初始化階段,完成緩存的實例化,并依賴注入到客戶端
cache:=NewCacheProxy(&memoryDb{tables:sync.Map{}})
client(cache)
}

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

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

總結實現代理模式的幾個關鍵點:

定義代理對象,實現被代理對象的接口。本例子中,前者是CacheProxy對象,后者是Db接口。

代理對象組合被代理對象,這里組合的應該是抽象接口,讓代理的可擴展性更高些。本例子中,CacheProxy對象組合了Db接口。

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

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

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

擴展

Go 標準庫中的反向代理

代理模式最典型的應用場景是遠程代理,其中,反向代理又是最常用的一種。

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

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

Go 標準庫的 net 包也提供了反向代理,ReverseProxy,位于net/http/httputil/reverseproxy.go下,實現http.Handler接口。http.Handler提供了處理 Http 請求的能力,也即相當于 Http 服務器。那么,對應到 UML 結構圖中,http.Handler就是 Subject,ReverseProxy就是 Proxy:

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

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

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

typeReverseProxystruct{
//修改前端請求,然后通過Transport將修改后的請求轉發給后端
Directorfunc(*http.Request)
//可理解為Subject,通過Transport來調用被代理對象的ServeHTTP方法處理請求
Transporthttp.RoundTripper
//修改后端響應,并將修改后的響應返回給前端
ModifyResponsefunc(*http.Response)error
//錯誤處理
ErrorHandlerfunc(http.ResponseWriter,*http.Request,error)
...
}

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

ReverseProxy就是典型的代理模式實現,其中,遠程代理無法直接引用后端的對象引用,因此這里通過引入Transport來遠程訪問后端服務,可以將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")
}

典型應用場景

遠程代理(remote proxy),遠程代理適用于提供服務的對象處在遠程的機器上,通過普通的函數調用無法使用服務,需要經過遠程代理來完成。因為并不能直接訪問本體對象,所有遠程代理對象通常不會直接持有本體對象的引用,而是持有遠端機器的地址,通過網絡協議去訪問本體對象

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

保護代理(protection proxy),保護代理用于控制對本體對象的訪問,常用于需要給 Client 的訪問加上權限驗證的場景。

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

智能引用(smart reference),智能引用為本體對象的訪問提供了額外的動作,常見的實現為 C++ 中的智能指針,為對象的訪問提供了計數功能,當訪問對象的計數為 0 時銷毀該對象。

優缺點

優點

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

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

缺點

作為遠程代理時,因為多了一次轉發,會影響請求的時延。

與其他模式的關聯

從結構上看,裝飾模式和 代理模式 具有很高的相似性,但是兩種所強調的點不一樣。前者強調的是為本體對象添加新的功能,后者強調的是對本體對象的訪問控制

文章配圖

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

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 代碼
    +關注

    關注

    30

    文章

    4828

    瀏覽量

    69055
  • 設計模式
    +關注

    關注

    0

    文章

    53

    瀏覽量

    8655
  • Client
    +關注

    關注

    0

    文章

    10

    瀏覽量

    8828
  • 代理模式
    +關注

    關注

    0

    文章

    4

    瀏覽量

    1789

原文標題:【Go實現】實踐GoF的23種設計模式:代理模式

文章出處:【微信號:yuanrunzi,微信公眾號:元閏子的邀請】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    適配器模式代理模式的區別

      代理模式  組成:  抽象角色:通過接口或抽象類聲明真實角色實現的業務方法。  代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,并可以附加自己的
    發表于 10-22 15:17

    23種基本的設計模式總結

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

    Command模式與動態語言

    Gof的設計模式中,有一個模式引起的爭議比較大,有很多人甚至認為這個模式應該排除在OO模式之外,原因在于它不具有OO的特性
    發表于 06-22 10:20 ?1042次閱讀
    Command<b class='flag-5'>模式</b>與動態語言

    Modbus ASCII 模式通訊程序

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

    適配器模式、裝飾器模式代理模式的區別

    適配器模式、裝飾器模式代理模式都屬于設計模式中的結構型模式,結構型設計
    發表于 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>的區別

    適配器模式代理模式的區別

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

    C語言設計模式的程序資料合集

    本文檔的主要內容詳細介紹的是C語言設計模式的程序資料合集包括了:C語言設計模式_繼承-多態-封裝,C語言設計模式_單件
    發表于 11-16 08:00 ?5次下載

    嵌入式軟件設計設計模式

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

    GoF設計模式觀察者模式

    現在有 2 個服務,Service A 和 Service B,通過 REST 接口通信;Service A 在某個業務場景下調用 Service B 的接口完成一個計算密集型任務,假設接口為 http://service_b/api/v1/domain;該任務運行時間很長,但 Service A 不想一直阻塞在接口調用上。為了滿足 Service A 的要求,通常有 2 種方案:
    的頭像 發表于 07-25 11:32 ?1069次閱讀

    GoF設計模式訪問者模式

    訪問者模式的目的是,解耦數據結構和算法,使得系統能夠在不改變現有代碼結構的基礎上,為對象新增一種新的操作。
    的頭像 發表于 10-08 11:05 ?740次閱讀

    實踐GoF的23種設計模式:命令模式簡介

    因此,我們需要對請求進行抽象,將上下文信息封裝到請求對象里,這其實就是命令模式,而該請求對象就是 Command。
    的頭像 發表于 01-13 16:36 ?908次閱讀

    設計模式結構性:代理模式

    代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬于結構型模式
    的頭像 發表于 06-09 15:27 ?876次閱讀
    設計<b class='flag-5'>模式</b>結構性:<b class='flag-5'>代理</b><b class='flag-5'>模式</b>

    設計模式代理模式的使用場景

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

    實踐GoF的23種設計模式:備忘錄模式

    相對于代理模式、工廠模式等設計模式,備忘錄模式(Memento)在我們日常開發中出鏡率并不高,除了應用場景的限制之外,另一個原因,可能是備忘
    的頭像 發表于 11-25 09:05 ?598次閱讀
    實踐<b class='flag-5'>GoF</b>的23種設計<b class='flag-5'>模式</b>:備忘錄<b class='flag-5'>模式</b>

    實踐GoF的23種設計模式:解釋器模式

    解釋器模式(Interpreter Pattern)應該是 GoF 的 23 種設計模式中使用頻率最少的一種了,它的應用場景較為局限。
    的頭像 發表于 04-01 11:01 ?766次閱讀
    實踐<b class='flag-5'>GoF</b>的23種設計<b class='flag-5'>模式</b>:解釋器<b class='flag-5'>模式</b>
    定边县| 百家乐官网tt赌场娱乐网规则 | 金沙国际娱乐| 百家乐官网在线娱乐场| 网上百家乐真的假| 玉山县| 百家乐视频双扣游戏| 什么棋牌游戏能赚钱| 百家乐官网规律打法| 威尼斯人娱乐城代理| 百家乐官网代理博彩正网| 百家乐赌场破解方法| E乐博| 7位百家乐官网扑克桌| 云鼎娱乐场网址| V博百家乐官网的玩法技巧和规则| 大发888游戏下载官方下载| 网上百家乐官网怎么赌能赢钱| sz新全讯网xb112| 百家乐官网赌博导航| 黄金城百家乐手机版| 财神百家乐官网娱乐城| 百家乐五种路单规| 真人百家乐官网娱乐好玩| HG百家乐大转轮| 凯旋门百家乐官网技巧| 百家乐网上赌有作假吗| 百家乐官网最好的投注法| 网络百家乐证据| 澳门百家乐官网博彩能做到不输吗| 夜总会百家乐的玩法技巧和规则 | 豪杰百家乐官网现金网| 百家乐凯时娱乐网| 百家乐官网注册送彩金平台| 千亿娱百家乐的玩法技巧和规则| 赌场百家乐官网试玩| 大发888代充值| 百家乐官网游戏大| 百家乐改单| 大世界百家乐娱乐平台| 新思维百家乐官网投注法|