1. 程式人生 > >二十三種設計模式[12] - 代理模式(Proxy Pattern)

二十三種設計模式[12] - 代理模式(Proxy Pattern)

前言

       代理模式,屬於物件結構型模式。在《設計模式 - 可複用的面向物件軟體》一書中將之描述為“ 為其它物件提供一種代理以控制對這個物件的訪問 ”。

       在代理模式中,通常使用一個類來代表另一個類的功能,並由這個代理物件去控制原物件的引用。

結構

Proxy_1

  • Subjuet(公共介面):代理類和被代理類的公共介面,保證任何使用目標的地方都可以被代理類替換;
  • RealSubject(被代理類):代理類所代表的目標類;
  • Proxy(代理類):包含對目標類的引用,目標類的封裝;

場景

       在日常生活中,我們買賣房子、汽車、電子產品都是通過中介或銷售來進行交易,中介和銷售在本次交易中收取佣金作為提成。中介和銷售就是作為我們購買實際產品的代理。

       我們在使用目標類的函式時通常會執行一些額外的操作。根據操作職責的不同有如下常見代理:

    1. 遠端代理 Remote Proxy;
    2. 虛代理 Virtual Proxy;
    3. Copy-on-Write代理;
    4. 保護代理 Protection Or Access;
    5. Cache代理;
    6. 防火牆代理 Firewall;
    7. 智慧引用代理 Smart Reference;

示例

       以買房為例。在買房的過程中需要在辦理完各種相關手續後才能得到房子的所有權。在這種情況下通常會通過房產中介來辦理這些手續。我們將通過代理模式來實現這一交易過程。

  • 靜態代理

       靜態代理是代理模式中最基本的實現方式,下面我們將建立一個IHouse介面用來描述房屋交易的過程,以及實現了IHouse介面的HouseA、HouseB和HousProxy類來表示房屋實體以及房產中介。

Proxy_2

public interface IHouse
{
    void Buy();
}

public class HouseA : IHouse
{
    public void Buy()
    {
        Console.WriteLine("購買房屋A");
    }
}

public class HouseB : IHouse
{
    public void Buy()
    {
        Console.WriteLine("購買房屋B");
    }
}

public class HouseProxy : IHouse
{
    private IHouse _House = null;

    public HouseProxy(IHouse house)
    {
        this._House = house;
    } 

    public void Buy()
    {
        Console.WriteLine("收取中介費");
        Console.WriteLine("辦理各種過戶手續");
        this._House.Buy();
        Console.WriteLine("交易完成");
    }
}

static void Main(string[] args)
{
    IHouse houseA = new HouseA();
    IHouse houseAProxy = new HouseProxy(houseA);
    houseAProxy.Buy();

    Console.WriteLine();

    IHouse houseB = new HouseB();
    IHouse houseBProxy = new HouseProxy(houseB);
    houseBProxy.Buy();

    Console.ReadKey();
}
  • 動態代理

       動態代理是spring AOP(面向切面程式設計)的實現機制。它能夠幫助我們進一步減少程式中的重複程式碼,使每個類、每個函式更加單純。

       在動態代理中,我們不需要建立目標類的代理類,而是由系統根據目標類動態的生成一個代理。這使得我們不必為每個目標類或介面建立對應的代理類,而只需要為某類業務擴充套件去建立一個攔截器即可。實現方式如下

  • (1) System.Runtime.Remoting

Proxy_3

 public class HouseA : ContextBoundObject
 {
     public string Name { set; get; } = "房屋A";
     public void Buy()
     {
         Console.WriteLine($"購買{this.Name}");
     }
 }

public class HouseB : ContextBoundObject
{
    public string Name { set; get; } = "房屋B";
    public void Buy()
    {
        Console.WriteLine($"購買{this.Name}");
    }
}

public class DynamicAction
{
    public List<Action> BeforeActionList { set; get; } = new List<Action>();
    public List<Action> AfterActionList { set; get; } = new List<Action>();
}

public class DynamicProxy<T> : RealProxy where T : ContextBoundObject
{
    private T _target = default(T);
    private DynamicAction _dynamicAction = null;

    public DynamicProxy(DynamicAction action = null) : base(typeof(T))
    {
        this._target = (T)Activator.CreateInstance(typeof(T));
        this._dynamicAction = action;
    }

    public DynamicProxy(T obj, DynamicAction action = null) : base(typeof(T))
    {
        this._target = obj;
        this._dynamicAction = action;
    }

    public override IMessage Invoke(IMessage msg)
    {
        var reqMsg = msg as IMethodCallMessage;
        if (reqMsg == null)
        {
            return new ReturnMessage(new Exception("呼叫失敗"), null);
        }

        if (this._dynamicAction != null
            && this._dynamicAction.BeforeActionList != null
            && this._dynamicAction.BeforeActionList.Count > 0)
        {
            this._dynamicAction.BeforeActionList.ForEach(t => t());
        }

        var result = RemotingServices.ExecuteMessage(this._target, reqMsg);

        if (this._dynamicAction != null
            && this._dynamicAction.AfterActionList != null
            && this._dynamicAction.AfterActionList.Count > 0)
        {
            this._dynamicAction.AfterActionList.ForEach(t => t());
        }

        return result;
    }
}

public class ProxyFactory<T> where T : ContextBoundObject
{
    public T CreateProxy(DynamicAction action = null)
    {
        return (T)new DynamicProxy<T>(action).GetTransparentProxy();
    }

    public T CreateProxy(T obj, DynamicAction action = null)
    {
        return (T)new DynamicProxy<T>(obj, action).GetTransparentProxy();
    }
}

static void Main(string[] args)
{
    DynamicAction action = new DynamicAction();
    action.BeforeActionList.Add(new Action(() => Console.WriteLine("收取中介費")));
    action.BeforeActionList.Add(new Action(() => Console.WriteLine("辦理各種過戶手術")));
    action.AfterActionList.Add(new Action(() => Console.WriteLine("交易完成")));

    ProxyFactory<HouseA> factory = new ProxyFactory<HouseA>();
    HouseA houseA = factory.CreateProxy();
    houseA.Buy();
    Console.WriteLine("-----------------");

    houseA.Name = "房屋AAAAAAAAAAA";
    HouseA houseAA = factory.CreateProxy(houseA, action);
    houseAA.Buy();
    Console.WriteLine("-----------------");

    ProxyFactory<HouseB> factoryB = new ProxyFactory<HouseB>();
    HouseB houseB = factoryB.CreateProxy(action);
    houseB.Buy();
    Console.ReadKey();

}

image

  • (2) Castle開源元件

注:目標類的函式必須是虛擬函式。

public class HouseA
{
    public string Name { set; get; } = "房屋A";
    public virtual void Buy()
    {
        Console.WriteLine($"購買{this.Name}");
    }
}

public class HouseB
{
    public string Name { set; get; } = "房屋B";
    public virtual void Buy()
    {
        Console.WriteLine($"購買{this.Name}");
    }
}

public class ClassInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("收取中介費");
        Console.WriteLine("辦理各種過戶手續");
        invocation.Proceed();
        Console.WriteLine("交易完成");
    }
}

static void Main(string[] args)
{
    ProxyGenerator generator = new ProxyGenerator();
    HouseA houseA = generator.CreateClassProxy<HouseA>(new ClassInterceptor());
    houseA.Buy();
    Console.ReadKey();
}

image

補充

  • 代理與介面卡

       在介面卡模式(Adapter Pattern)中,主要目的是使介面的實現類能夠相容另一介面的某一實現類。而代理模式中,代理類與目標類實現了同一介面。並且在代理模式的某些變形狀態下可能會拒絕執行目標類的函式,比如保護代理。

  • 代理與裝飾

       裝飾模式(Decorator Pattern)與代理模式的實現類似,但裝飾模式的目的是為了在不改變目標類的前提下對其進行功能的擴充套件,且它的結構是遞迴組合的方式。一般情況下目標類只提供了部分功能,而其它Decorator負責完成其它功能。而在代理模式中,目標類定義了關鍵功能,Proxy提供(或拒絕)對它的訪問。

總結

       由於代理類的存在,使得我們不必關心當前操作的非核心目的(比如示例中的購房手續),同時也能夠使目標類的職責更加清晰、單純(House類只負責提供購買)。也正是由於代理的存在,可能會導致當前操作的處理速度變慢(動態代理由反射實現)。雖然代理模式能夠有效提高程式碼的重用性,但也使得程式的可讀性降低。

 

       以上,就是我對代理模式的理解,希望對你有所幫助。

       示例原始碼:https://gitee.com/wxingChen/DesignPatternsPractice

       系列彙總:https://www.cnblogs.com/wxingchen/p/10031592.html

       本文著作權歸本人所有,如需轉載請標明本文連結(https://www.cnblogs.com/wxingchen/p/10078627.html)