設計模式(十二)代理

2023-11-13 15:00:49

一、定義

給某一個物件提供一個代理或預留位置,並由代理物件控制對原物件的存取。代理模式是一種結構型模式。

二、描述

代理模式的結構比較簡單,其核心是代理類,為了讓使用者端能夠一致性地對待真實物件和代理物件,在代理模式中引入了抽象層。包含以下三個角色:

1、Subject(抽象主題角色):它宣告真實主題和代理主題的共同介面,這樣一來在任何使用真實主題的地方都可以使用代理主題,使用者端通常需要針對抽象主題角色進行程式設計。
2、Proxy(代理主題角色):它包含了對真實主題的參照,從而可以在任何時候操作真實主題物件;在代理主題角色中提供了一個與真實主題角色相同的介面,以便在任何時候都可以代替真實主題;代理主題角色還可以控制對真實主題的使用,負責在需要的時候建立和刪除真實主題物件,並對真實主題物件的使用加以約束。通常,在代理主題角色中,使用者端在呼叫所參照的真實主題操作之前或之後還需要執行其他操作,而不僅僅是單純地呼叫真實主題物件中的操作。
3、RealSubject(真實主題角色):它定義了代理角色所代表的真實物件,在真實主題角色中實現了真實的業務操作,使用者端可以通過代理主題角色間接呼叫真實主題角色中定義的操作。

三、例子

X公司公司承接了某資訊諮詢公司的收費商務資訊查詢系統的開發任務,該系統的基本需求如下:
(1)在進行商務資訊查詢之前使用者需要通過身份驗證,只有合法使用者才能夠使用該查詢系統。
(2)在進行商務資訊查詢時,系統需要記錄查詢紀錄檔,以便根據查詢次數收取查詢費用。
X公司開發人員已經完成了商務資訊查詢模組的開發任務,他們希望能夠以一種鬆耦合的方式向原有系統增加身份驗證和紀錄檔記錄功能,使用者端程式碼可以無區別地對待原始的商務資訊查詢模組和增加新功能之後的商務資訊查詢模組,而且可能在將來還要在該資訊查詢模組中增加一些新的功能。
ISearcher:抽象查詢介面,充當抽象主題類

public interface ISearcher
{
    string DoSearch(string userID, string keyword);
}

RealSearcher:具體查詢器,充當真實主題類

public  class RealSearcher
{
    /// <summary>
    /// 模擬查詢商務資訊
    /// </summary>
    /// <returns></returns>
    public string DoSearch(string userID, string keyword)
    {
        Console.WriteLine("{0} 使用關鍵詞 {1}", userID, keyword);
        return "返回具體內容";
    }
}

AccessValidator、Logger:身份驗證類、紀錄檔記錄類,業務類

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;
        }
    }
}

public class Logger
{
    /// <summary>
    /// 模擬實現紀錄檔記錄
    /// </summary>
    /// <param name="userID"></param>
    public void Log(string userID)
    {
        Console.WriteLine("更新資料庫,使用者 {0} 查詢次數加1!", userID);
    }
}

ProxySearcher:代理查詢類,充當代理主題類

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);
    }
}

Program:使用者端測試類

Searcher searcher = new ProxySearcher();
if (searcher != null)
{
    string result = searcher.DoSearch("楊過", "玉女心經");
}
Console.ReadLine();

四、總結

1、優點

(1)代理模式能夠協調呼叫者和被呼叫者,在一定程度上降低了系統的耦合度。
(2)使用者端可以針對抽象主題角色進行程式設計,增加和更換代理類無須修改原始碼,符合開閉原則,系統具有較好的靈活性和可延伸性。

2、缺點

(1)由於這使用者端的真實主題之間增加了代理物件,因此有些型別的代理模式可能會造成請求的處理速度變慢,例如保護代理。
(2)實現代理模式需要額外的工作,而且有些代理模式的實現過程較為複雜,例如遠端代理。