天天看點

設計模式的征途—13.代理(Proxy)模式

設計模式的征途—13.代理(Proxy)模式

所謂代購,簡單說來就是找人幫忙購買所需要的商品。代購分為兩種類型,一種是因為在當地買不到某件商品,又或者是因為當地這件商品的價格比其他地區的貴,是以托人在其他地區甚至國外購買該商品,然後通過快遞發貨或直接攜帶回來。另一種則是消費者對想要購買的商品相關資訊的缺乏,自己無法确定其實際價值,是以隻好委托中介講價或購買。在軟體開發中,有一種設計模式可以提供與代購類似的功能,由于某些原因,用戶端不想或者不能直接通路某個對象,此時可以通過一個稱之為“代理”的第三者來實作間接通路,該方案對應的設計模式則被稱為代理模式。

代理模式(Proxy) 學習難度:★★★☆☆ 使用頻率:★★★★☆

一、收費商務查詢系統的設計

M公司承接了某資訊咨詢公司的收費商務資訊查詢系統的開發任務,該系統的基本需求如下:

(1)在進行商務資訊查詢之前使用者需要通過身份驗證,隻有合法使用者才能夠使用該查詢系統。

(2)在進行商務資訊查詢時,系統需要記錄查詢日志,以便根據查詢次數收取查詢費用。

M公司開發人員已經完成了商務資訊查詢子產品的開發任務,他們希望能夠以一種松耦合的方式向原有系統增加身份驗證和日志記錄功能,用戶端代碼可以無差別地對待原始的商務資訊查詢子產品和增加新功能之後的商務資訊查詢子產品,而且可能在将來還要在該資訊查詢子產品中增加一些新的功能。

  M公司開發人員通過分析,決定采用一種間接通路的方式來實作該商務資訊查詢系統的設計,在用戶端對象和資訊查詢對象之間增加一個代理對象,讓代理對象來實作驗證和日志記錄功能,而無須直接對原有的商務資訊查詢對象進行修改,如下圖所示:

設計模式的征途—13.代理(Proxy)模式

  這種設計方案即為代理模式,它為對象的通路提供了一種設計方案,而且具有多種不同的類型,應用相當廣泛。

二、代理模式概述

2.1 代理模式簡介

代理(Proxy)模式:給某一個對象提供一個代理,并由代理對象控制對原對象的引用。代理模式是一種對象結構型模式。

  可以看重,代理模式的重點就在于引入了一個新的代理對象,代理對象可以在用戶端對象和目标對象之間起到中介的作用,去掉客戶不能看到的内容和服務或者添加客戶需要的額外服務。

2.2 代理模式結構

設計模式的征途—13.代理(Proxy)模式

  代理模式主要包含以下3個角色:

  (1)Subject(抽象主題角色):聲明真實主題和代理主題的共同接口,使得在任何使用真實主題的地方都可以使用代理主題。

  (2)Proxy(代理主題角色):代理主題角色内部包含了對真實主題的引用,進而可以在任何時候操作真實主題對象;

  (3)RealSubject(真實主題角色):定義了代理角色所代表的真實對象,在真實主題角色中實作了真實的業務操作。

三、實作收費商務查詢系統

3.1 系統設計結構

設計模式的征途—13.代理(Proxy)模式

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主要用于通路配置檔案并通過反射生成執行個體對象

設計模式的征途—13.代理(Proxy)模式
設計模式的征途—13.代理(Proxy)模式
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

  ③ 運作結果

  

設計模式的征途—13.代理(Proxy)模式

四、代理模式總結

4.1 主要優點

  (1)協調了調用者和被調用者,一定程度上降低了系統的耦合度 => 符合迪米特法則

  (2)用戶端針對抽象主題角色程式設計,增加和更換代理類無須修改源代碼 => 符合開閉原則

4.2 應用場景

  (1)用戶端需要通路遠端主機中的對象時 => 遠端代理

  (2)需要一個消耗資源較少的對象來代表一個消耗資源較多的對象 => 降低系統開銷

  (3)需要控制對一個對象的通路,為不同使用者提供不同級别的通路權限 => 保護代理

參考資料

設計模式的征途—13.代理(Proxy)模式

  劉偉,《設計模式的藝術—軟體開發人員内功修煉之道》

作者:周旭龍

出處:http://edisonchou.cnblogs.com

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。

繼續閱讀