設計模式的征途—13.代理(Proxy)模式
所謂代購,簡單說來就是找人幫忙購買所需要的商品。代購分為兩種型別,一種是因為在當地買不到某件商品,又或者是因為當地這件商品的價格比其他地區的貴,因此託人在其他地區甚至國外購買該商品,然後通過快遞發貨或直接攜帶回來。另一種則是消費者對想要購買的商品相關資訊的缺乏,自己無法確定其實際價值,因此只好委託中介講價或購買。在軟體開發中,有一種設計模式可以提供與代購類似的功能,由於某些原因,客戶端不想或者不能直接訪問某個物件,此時可以通過一個稱之為“代理”的第三者來實現間接訪問,該方案對應的設計模式則被稱為代理模式。
外觀模式(Proxy) | 學習難度:★★★☆☆ | 使用頻率:★★★★☆ |
一、收費商務查詢系統的設計
M公司承接了某資訊諮詢公司的收費商務資訊查詢系統的開發任務,該系統的基本需求如下:
(1)在進行商務資訊查詢之前使用者需要通過身份驗證,只有合法使用者才能夠使用該查詢系統。
(2)在進行商務資訊查詢時,系統需要記錄查詢日誌,以便根據查詢次數收取查詢費用。
M公司開發人員已經完成了商務資訊查詢模組的開發任務,他們希望能夠以一種鬆耦合的方式向原有系統增加身份驗證和日誌記錄功能,客戶端程式碼可以無區別地對待原始的商務資訊查詢模組和增加新功能之後的商務資訊查詢模組,而且可能在將來還要在該資訊查詢模組中增加一些新的功能。
M公司開發人員通過分析,決定採用一種間接訪問的方式來實現該商務資訊查詢系統的設計,在客戶端物件和資訊查詢物件之間增加一個代理物件,讓代理物件來實現驗證和日誌記錄功能,而無須直接對原有的商務資訊查詢物件進行修改,如下圖所示:
這種設計方案即為代理模式,它為物件的訪問提供了一種設計方案,而且具有多種不同的型別,應用相當廣泛。
二、代理模式概述
2.1 代理模式簡介
代理(Proxy)模式:給某一個物件提供一個代理,並由代理物件控制對原物件的引用。代理模式是一種物件結構型模式。
可以看重,代理模式的重點就在於引入了一個新的代理物件,代理物件可以在客戶端物件和目標物件之間起到中介的作用,去掉客戶不能看到的內容和服務或者新增客戶需要的額外服務。
2.2 代理模式結構
代理模式主要包含以下3個角色:
(1)Subject(抽象主題角色):宣告真實主題和代理主題的共同介面,使得在任何使用真實主題的地方都可以使用代理主題。
(2)Proxy(代理主題角色):代理主題角色內部包含了對真實主題的引用,從而可以在任何時候操作真實主題物件;
(3)RealSubject(真實主題角色):定義了代理角色所代表的真實物件,在真實主題角色中實現了真實的業務操作。
三、實現收費商務查詢系統
3.1 系統設計結構
3.2 具體程式碼實現
(1)抽象主題 => ISearcher介面
/// <summary> /// 抽象主題類:抽象查詢介面 /// </summary> public interface ISearcher { string DoSearch(string userID, string keyword); }
(2)真實主題 => RealSearcher類
/// <summary> /// 真是主題類:具體查詢器 /// </summary> public class RealSearcher { /// <summary> /// 模擬查詢商務資訊 /// </summary> /// <returns></returns> public string DoSearch(string userID, string keyword) { Console.WriteLine("{0} 使用關鍵詞 {1}", userID, keyword); return "返回具體內容"; } }
此外,還有兩個業務類:AccessValidator用於驗證使用者身份,Logger則用於記錄日誌。
/// <summary> /// 業務類:身份驗證類 /// </summary> public class AccessValidator { /// <summary> /// 模擬實現登入驗證 /// </summary> /// <param name="userID"></param> /// <returns></returns> public bool Validate(string userID) { Console.WriteLine("在資料庫中驗證使用者 {0} 是否是合法使用者?", userID); if (userID.Equals("楊過", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("{0} 登入成功!", userID); return true; } else { Console.WriteLine("{0} 登入失敗!", userID); return false; } } } /// <summary> /// 業務類:日誌記錄類 /// </summary> public class Logger { /// <summary> /// 模擬實現日誌記錄 /// </summary> /// <param name="userID"></param> public void Log(string userID) { Console.WriteLine("更新資料庫,使用者 {0} 查詢次數加1!", userID); } }
(3)代理主題 => ProxySearcher類
/// <summary> /// 代理主題類:代理查詢 /// </summary> public class ProxySearcher : ISearcher { private RealSearcher searcher = new RealSearcher(); // 維持一個對真實主題的引用 private AccessValidator validator; private Logger logger; public string DoSearch(string userID, string keyword) { if (Validate(userID)) { string result = searcher.DoSearch(userID, keyword); this.Log(userID); return result; } return null; } /// <summary> /// 建立訪問驗證物件並呼叫其Validate()方法進行身份驗證 /// </summary> /// <returns></returns> public bool Validate(string userID) { validator = new AccessValidator(); return validator.Validate(userID); } /// <summary> /// 建立日誌記錄器並呼叫Log()方法實現日誌記錄 /// </summary> /// <param name="userID"></param> public void Log(string userID) { logger = new Logger(); logger.Log(userID); } }
(4)客戶端呼叫
① 為了提高系統可擴充套件性,這裡將代理主題類存在了配置檔案中
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!-- Proxy Setting --> <add key="ProxyName" value="Manulife.ChengDu.DesignPattern.Proxy.ProxySearcher, Manulife.ChengDu.DesignPattern.Proxy" /> </appSettings> </configuration>
② 客戶端除錯程式碼
public class Program { public static void Main(string[] args) { ISearcher searcher = AppConfigHelper.GetProxyInstance() as ISearcher; if (searcher != null) { string result = searcher.DoSearch("楊過", "玉女心經"); } Console.ReadKey(); } }
這裡AppConfigHelper主要用於訪問配置檔案並通過反射生成例項物件
public class AppConfigHelper { public static string GetProxyName() { string factoryName = null; try { factoryName = System.Configuration.ConfigurationManager.AppSettings["ProxyName"]; } catch (Exception ex) { Console.WriteLine(ex.Message); } return factoryName; } public static object GetProxyInstance() { string assemblyName = AppConfigHelper.GetProxyName(); Type type = Type.GetType(assemblyName); var instance = Activator.CreateInstance(type); return instance; } }View Code
③ 執行結果
四、代理模式總結
4.1 主要優點
(1)協調了呼叫者和被呼叫者,一定程度上降低了系統的耦合度 => 符合迪米特法則
(2)客戶端針對抽象主題角色程式設計,增加和更換代理類無須修改原始碼 => 符合開閉原則
4.2 應用場景
(1)客戶端需要訪問遠端主機中的物件時 => 遠端代理
(2)需要一個消耗資源較少的物件來代表一個消耗資源較多的物件 => 降低系統開銷
(3)需要控制對一個物件的訪問,為不同使用者提供不同級別的訪問許可權 => 保護代理
參考資料
劉偉,《設計模式的藝術—軟體開發人員內功修煉之道》
作者:周旭龍
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。
相關推薦
設計模式的征途—13.代理(Proxy)模式
所謂代購,簡單說來就是找人幫忙購買所需要的商品。代購分為兩種型別,一種是因為在當地買不到某件商品,又或者是因為當地這件商品的價格比其他地區的貴,因此託人在其他地區甚至國外購買該商品,然後通過快遞發貨或直接攜帶回來。另一種則是消費者對想要購買的商品相關資訊的缺乏,自己無法確定其實際價值,因此只好委託中介講價或購
【java設計模式】之 代理(Proxy)模式
代理模式的核心作用就是通過代理,控制對物件的訪問。這跟實際中是一樣的,比如說明星都有經紀人,這就是一個代理,比如有人要找某明星拍戲,那麼首先處理這事的是他的經紀人,雖然拍戲需要自己拍,但是拍戲前後的一些必須要做的事等等,都由這個經紀人來處理。 在程式中也是如此,通過
程式設計模式(十二) C++ 代理(Proxy)模式
2.7 Proxy 代理模式為其他物件提供一種代理以控制對這個物件的訪問。 在需要用比較通用和複雜的物件指標代替簡單的的指標的時候,使用代理模式。有四種常用的情況: 1、遠端代理,也就是為一個物件在不同的地址空間提供區域性代表。這樣可以隱藏一個物件存在於不
設計模式(12)—— 結構型 ——代理(Proxy)
介紹 介紹:為其它物件提供一種代理,以控制對這個物件的訪問 說明:代理物件在客戶端和目標物件之間起到中介作用 適用場景: 保護目標物件 增強目標物件 優點: 將代理物件
詳淡設計模式-代理(Proxy)
文章目錄 代理模式(英語:Proxy Pattern) 1.靜態代理 1.1 繼承 1.2 聚合 2.動態代理 2.1 jdk動態代理
設計模式(9)—— 代理(Proxy)
介紹 介紹:為其它物件提供一種代理,以控制對這個物件的訪問 說明:代理物件在客戶端和目標物件之間起到中介作用 適用場景: 保護目標物件 增強目標物件 優點: 將代理物件與真實被呼叫那個的目標
代理(Proxy)設計模式
概述 正文開始之前我們先考慮一個問題:什麼叫做代理(Proxy)? 按照維基百科定義: 代理(英語:Proxy)也稱網路代理,是一種特殊的網路服務,允許一個網路終端(一般為客戶端)通過這個服務與另一個網路終端(一般為伺服器)進行非直接的連線。一些閘道器、路由器等網路裝置具備網路代理功能。一般認為代理服務有
設計模式的征途—5.原型(Prototype)模式
pla width 共享 太多的 isp text 一模一樣 軟件 集合 相信大多數的人都看過《西遊記》,對孫悟空拔毛變出小猴子的故事情節應該都很熟悉。孫悟空可以用猴毛根據自己的形象復制出很多跟自己一模一樣的小猴兵出來,其實在設計模式中也有一個類似的模式,我們可以通過一個原
設計模式的征途—16.訪問者(Visitor)模式
lose mar rtm image 3.1 conf 系統 .get 封裝性 在患者就醫時,醫生會根據病情開具處方單,很多醫院都會存在以下這個流程:劃價人員拿到處方單之後根據藥品名稱和數量計算總價,而藥房工作人員根據藥品名稱和數量準備藥品,如下圖所示。 在軟件開發中
設計模式的征途—18.策略(Strategy)模式
滿足 應用 基礎 blog title pla 生成 display 多個 俗話說條條大路通羅馬,很多情況下實現某個目標地途徑都不只一條。在軟件開發中,也會時常遇到這樣的情況,實現某一個功能有多條途徑,每一條途徑都對應一種算法。此時,可以使用一種設計模式來實現靈活地選擇解決
設計模式的征途—20.備忘錄(Memento)模式
行為 修煉之道 mda 3.2 ima 位置 pri 捕獲 spl 相信每個人都有後悔的時候,但是人生並無後悔藥,有些錯誤一旦發生就無法再挽回,有些事一旦錯過就不會再重來,有些話一旦說出口也就不可能再收回,這就是人生。為了不讓自己後悔,我們總是需要三思而後行。這裏我們要學習
設計模式的征途—10.裝飾(Decorator)模式
雖然目前房價依舊很高,就連我所在的成都郊區(非中心城區)的房價均價都早已破萬,但卻還是阻擋不了大家對新房的渴望和買房的熱情。如果大家買的是清水房,那麼無疑還有一項艱鉅的任務在等著大家,那就是裝修。對新房的裝修並沒有改變房屋用於居住的本質,但它可以讓房子變得更加漂亮和溫馨以及更加實用。在軟體設計中,也有一種類似
設計模式的征途—11.外觀(Facade)模式
在軟體開發中,有時候為了完成一項較為複雜的功能,一個類需要和多個其他業務類互動,而這些需要互動的業務類經常會作為一個完整的整體出現,由於涉及的類比較多,導致使用時程式碼較為複雜,此時,特別需要一個類似服務員一樣的角色,由他來負責和多個業務類進行互動,而使用這些業務類的類只需要和該類進行互動即可。外觀模式通過引
設計模式的征途—19.命令(Command)模式
在生活中,我們裝修新房的最後幾道工序之一是安裝插座和開關,通過開關可以控制一些電器的開啟和關閉,例如電燈或換氣扇。在購買開關時,使用者並不知道它將來到底用於控制什麼電器,也就是說,開關與電燈、換氣扇並無直接關係,一個開關在安裝之後可能用來控制電燈,也可能用來控制換氣扇或者其他電器裝置。相同的開關可以通過不同的
設計模式的征途—23.直譯器(Interpreter)模式
雖然目前計算機程式語言有好幾百種,但有時人們還是希望用一些簡單的語言來實現特定的操作,只需要向計算機輸入一個句子或檔案,就能按照預定的文法規則來對句子或檔案進行解釋。例如,我們想要只輸入一個加法/減法表示式,它就能夠計算出表示式結果。例如輸入“1+2+3-4+1”時,將輸出計算結果為3。像C++,Java或C
設計模式的征途—7.介面卡(Adapter)模式
在現實生活中,我們的膝上型電腦的工作電壓大多數都是20V,而我國的家庭用電是220V,如何讓20V的膝上型電腦能夠工作在220V的電壓下工作?答案:引入一個電源介面卡,俗稱變壓器,有了這個電源介面卡,生活用電和膝上型電腦即可相容。 在軟體開發中,有時候也會存在這種不相容的情況,我們也可以像電源介面卡一樣引入
設計模式:裝飾器(Decorator)模式
讓我 分享圖片 底部 .com 一件事 輸出 PE 新的 int 設計模式:裝飾器(Decorator)模式 一、前言 裝飾器模式也是一種非常重要的模式,在Java以及程序設計中占據著重要的地位。比如Java的數據流處理,我們可能看到數據流經過不同的類的包裝和包裹,最
設計模式:觀察者(Observer)模式
image 強制轉換 trace vat PE sta obs observer -a 設計模式:觀察者(Observer)模式 一、前言 觀察者模式其實最好的名稱應該是“發布訂閱”模式,和我們現在大數據之中的發布訂閱方式比較類似,但是也有區別的地方,在上一個設計模式,
設計模式:享元(FlyWeight)模式
例子 清理 什麽 public == lean http 變量 -- 設計模式:享元(FlyWeight)模式 一、前言 享元(FlyWeight)模式顧名思義,既是輕量級的,原因就是享元,共享元素,這裏的元素指的是對象。如何共享對象,那就是在檢測對象產生的時候,如
Koffee設計模式學習之路(一) —— 模式學習總結思路
這篇部落格沒有相關技術細節,僅作為自己對設計模式這個東西的一點感悟和以後設計模式系列部落格的一個寫作思路。 作為非科班出身,誤打誤撞進入程式設計的人,在上研究生期間對於程式的唯一要求就是:能用。彼時,不知道有面向物件,記憶體管理,多執行緒,