Go設計模式之代理模式圖文詳解
代理模式
亦稱: Proxy
代理模式是一種結構型設計模式, 讓你能夠提供對象的替代品或其占位符。 代理控制著對于原對象的訪問, 并允許在將請求提交給對象前后進行一些處理。
問題
為什么要控制對于某個對象的訪問呢? 舉個例子: 有這樣一個消耗大量系統(tǒng)資源的巨型對象, 你只是偶爾需要使用它, 并非總是需要。
數(shù)據(jù)庫查詢有可能會非常緩慢。
你可以實現(xiàn)延遲初始化: 在實際有需要時再創(chuàng)建該對象。 對象的所有客戶端都要執(zhí)行延遲初始代碼。 不幸的是, 這很可能會帶來很多重復代碼。
在理想情況下, 我們希望將代碼直接放入對象的類中, 但這并非總是能實現(xiàn): 比如類可能是第三方封閉庫的一部分。
解決方案
代理模式建議新建一個與原服務對象接口相同的代理類, 然后更新應用以將代理對象傳遞給所有原始對象客戶端。 代理類接收到客戶端請求后會創(chuàng)建實際的服務對象, 并將所有工作委派給它。
代理將自己偽裝成數(shù)據(jù)庫對象, 可在客戶端或實際數(shù)據(jù)庫對象不知情的情況下處理延遲初始化和緩存查詢結果的工作。
這有什么好處呢? 如果需要在類的主要業(yè)務邏輯前后執(zhí)行一些工作, 你無需修改類就能完成這項工作。 由于代理實現(xiàn)的接口與原類相同, 因此你可將其傳遞給任何一個使用實際服務對象的客戶端。
真實世界類比
信用卡和現(xiàn)金在支付過程中的用處相同。
信用卡是銀行賬戶的代理, 銀行賬戶則是一大捆現(xiàn)金的代理。 它們都實現(xiàn)了同樣的接口, 均可用于進行支付。 消費者會非常滿意, 因為不必隨身攜帶大量現(xiàn)金; 商店老板同樣會十分高興, 因為交易收入能以電子化的方式進入商店的銀行賬戶中, 無需擔心存款時出現(xiàn)現(xiàn)金丟失或被搶劫的情況。
代理模式結構
服務接口 (Service Interface) 聲明了服務接口。 代理必須遵循該接口才能偽裝成服務對象。
服務 (Service) 類提供了一些實用的業(yè)務邏輯。
代理 (Proxy) 類包含一個指向服務對象的引用成員變量。 代理完成其任務 (例如延遲初始化、 記錄日志、 訪問控制和緩存等) 后會將請求傳遞給服務對象。
通常情況下, 代理會對其服務對象的整個生命周期進行管理。
客戶端 (Client) 能通過同一接口與服務或代理進行交互, 所以你可在一切需要服務對象的代碼中使用代理。
偽代碼
本例演示如何使用代理模式在第三方騰訊視頻 (TencentVideo, 代碼示例中記為 TV) 程序庫中添加延遲初始化和緩存。
使用代理緩沖服務結果。
程序庫提供了視頻下載類。 但是該類的效率非常低。 如果客戶端程序多次請求同一視頻, 程序庫會反復下載該視頻, 而不會將首次下載的文件緩存下來復用。
代理類實現(xiàn)和原下載器相同的接口, 并將所有工作委派給原下載器。 不過, 代理類會保存所有的文件下載記錄, 如果程序多次請求同一文件, 它會返回緩存的文件。
// 遠程服務接口。 interface ThirdPartyTVLib is method listVideos() method getVideoInfo(id) method downloadVideo(id) // 服務連接器的具體實現(xiàn)。該類的方法可以向騰訊視頻請求信息。請求速度取決于 // 用戶和騰訊視頻的互聯(lián)網(wǎng)連接情況。如果同時發(fā)送大量請求,即使所請求的信息 // 一模一樣,程序的速度依然會減慢。 class ThirdPartyTVClass implements ThirdPartyTVLib is method listVideos() is // 向騰訊視頻發(fā)送一個 API 請求。 method getVideoInfo(id) is // 獲取某個視頻的元數(shù)據(jù)。 method downloadVideo(id) is // 從騰訊視頻下載一個視頻文件。 // 為了節(jié)省網(wǎng)絡帶寬,我們可以將請求結果緩存下來并保存一段時間。但你可能無 // 法直接將這些代碼放入服務類中。比如該類可能是第三方程序庫的一部分或其簽 // 名是`final(最終)`。因此我們會在一個實現(xiàn)了服務類接口的新代理類中放入 // 緩存代碼。當代理類接收到真實請求后,才會將其委派給服務對象。 class CachedTVClass implements ThirdPartyTVLib is private field service: ThirdPartyTVLib private field listCache, videoCache field needReset constructor CachedTVClass(service: ThirdPartyTVLib) is this.service = service method listVideos() is if (listCache == null || needReset) listCache = service.listVideos() return listCache method getVideoInfo(id) is if (videoCache == null || needReset) videoCache = service.getVideoInfo(id) return videoCache method downloadVideo(id) is if (!downloadExists(id) || needReset) service.downloadVideo(id) // 之前直接與服務對象交互的 GUI 類不需要改變,前提是它僅通過接口與服務對 // 象交互。我們可以安全地傳遞一個代理對象來代替真實服務對象,因為它們都實 // 現(xiàn)了相同的接口。 class TVManager is protected field service: ThirdPartyTVLib constructor TVManager(service: ThirdPartyTVLib) is this.service = service method renderVideoPage(id) is info = service.getVideoInfo(id) // 渲染視頻頁面。 method renderListPanel() is list = service.listVideos() // 渲染視頻縮略圖列表。 method reactOnUserInput() is renderVideoPage() renderListPanel() // 程序可在運行時對代理進行配置。 class Application is method init() is aTVService = new ThirdPartyTVClass() aTVProxy = new CachedTVClass(aTVService) manager = new TVManager(aTVProxy) manager.reactOnUserInput()
代理模式適合應用場景
使用代理模式的方式多種多樣, 我們來看看最常見的幾種。
延遲初始化 (虛擬代理)。 如果你有一個偶爾使用的重量級服務對象, 一直保持該對象運行會消耗系統(tǒng)資源時, 可使用代理模式。
你無需在程序啟動時就創(chuàng)建該對象, 可將對象的初始化延遲到真正有需要的時候。
訪問控制 (保護代理)。 如果你只希望特定客戶端使用服務對象, 這里的對象可以是操作系統(tǒng)中非常重要的部分, 而客戶端則是各種已啟動的程序 (包括惡意程序), 此時可使用代理模式。
代理可僅在客戶端憑據(jù)滿足要求時將請求傳遞給服務對象。
本地執(zhí)行遠程服務 (遠程代理)。 適用于服務對象位于遠程服務器上的情形。在這種情形中, 代理通過網(wǎng)絡傳遞客戶端請求, 負責處理所有與網(wǎng)絡相關的復雜細節(jié)。
記錄日志請求 (日志記錄代理)。 適用于當你需要保存對于服務對象的請求歷史記錄時。代理可以在向服務傳遞請求前進行記錄。
緩存請求結果 (緩存代理)。 適用于需要緩存客戶請求結果并對緩存生命周期進行管理時, 特別是當返回結果的體積非常大時。代理可對重復請求所需的相同結果進行緩存, 還可使用請求參數(shù)作為索引緩存的鍵值。
智能引用。 可在沒有客戶端使用某個重量級對象時立即銷毀該對象。代理會將所有獲取了指向服務對象或其結果的客戶端記錄在案。 代理會時不時地遍歷各個客戶端, 檢查它們是否仍在運行。 如果相應的客戶端列表為空, 代理就會銷毀該服務對象, 釋放底層系統(tǒng)資源。
代理還可以記錄客戶端是否修改了服務對象。 其他客戶端還可以復用未修改的對象。
實現(xiàn)方式
- 如果沒有現(xiàn)成的服務接口, 你就需要創(chuàng)建一個接口來實現(xiàn)代理和服務對象的可交換性。 從服務類中抽取接口并非總是可行的, 因為你需要對服務的所有客戶端進行修改, 讓它們使用接口。 備選計劃是將代理作為服務類的子類, 這樣代理就能繼承服務的所有接口了。
- 創(chuàng)建代理類, 其中必須包含一個存儲指向服務的引用的成員變量。 通常情況下, 代理負責創(chuàng)建服務并對其整個生命周期進行管理。 在一些特殊情況下, 客戶端會通過構造函數(shù)將服務傳遞給代理。
- 根據(jù)需求實現(xiàn)代理方法。 在大部分情況下, 代理在完成一些任務后應將工作委派給服務對象。
- 可以考慮新建一個構建方法來判斷客戶端可獲取的是代理還是實際服務。 你可以在代理類中創(chuàng)建一個簡單的靜態(tài)方法, 也可以創(chuàng)建一個完整的工廠方法。
- 可以考慮為服務對象實現(xiàn)延遲初始化。
代理模式優(yōu)缺點
你可以在客戶端毫無察覺的情況下控制服務對象。
如果客戶端對服務對象的生命周期沒有特殊要求, 你可以對生命周期進行管理。
即使服務對象還未準備好或不存在, 代理也可以正常工作。
開閉原則。 你可以在不對服務或客戶端做出修改的情況下創(chuàng)建新代理。
代碼可能會變得復雜, 因為需要新建許多類。
服務響應可能會延遲。
代碼示例
Go設計模式之代理模式講解和代碼示例_Golang_腳本之家 (jb51.net)
到此這篇關于Go設計模式之代理模式圖文詳解的文章就介紹到這了,更多相關Go代理模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang結合mysql設置最大連接數(shù)和最大空閑連接數(shù)
本文介紹golang?中連接MySQL時,如何設置最大連接數(shù)和最大空閑連接數(shù),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02Golang實現(xiàn)短網(wǎng)址/短鏈服務的開發(fā)筆記分享
這篇文章主要為大家詳細介紹了如何使用Golang實現(xiàn)短網(wǎng)址/短鏈服務,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解一下2023-05-05基于Golang實現(xiàn)內(nèi)存數(shù)據(jù)庫的示例詳解
這篇文章主要為大家詳細介紹了如何基于Golang實現(xiàn)內(nèi)存數(shù)據(jù)庫,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的小伙伴可以參考一下2023-03-03golang解析json數(shù)據(jù)的4種方法總結
在日常工作中每一名開發(fā)者,不管是前端還是后端,都經(jīng)常使用 JSON,下面這篇文章主要給大家介紹了關于golang解析json數(shù)據(jù)的4種方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-06-06