Loading

结构型模式之代理模式

在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。

定义与特点

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介

主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
  • 代理对象可以扩展目标对象的功能
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度

主要缺点有:

  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
  • 增加了系统的复杂度

结构与实现

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。

模式的结构

代理模式的主要角色如下:

  • 抽象主题(Subject)类:通过接口或抽象类(推荐使用接口)声明真实主题和代理对象实现的业务方法
  • 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象
  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能

其结构图如图所示:

模式的实现

代理模式的实现代码如下:

//访问类
class Program
{
    static void Main(string[] args)
    {
        //代理模式        
        Proxy proxy=new Proxy();
        proxy.Request();       
        Console.ReadKey();
    }
}

//抽象主题
public interface ISubject
{
    void Request();
}

//真实主题
public class RealSubject :ISubject
{
    public void Request()
    {
        Console.WriteLine("访问真实主题方法...");
    }
}

//代理
public class Proxy : ISubject
{
    private RealSubject realSubject;
    public void Request()
    {
        if (realSubject==null)
        {
            realSubject=new RealSubject();
        }
        PreRequest();
        realSubject.Request();
        PostRequest();
    }
    public void PreRequest()
    {
        Console.WriteLine("访问真实主题之前的预处理。");
    }
    public void PostRequest()
    {
        Console.WriteLine("访问真实主题之后的后续处理。");
    }
}

程序运行的结果如下:

访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。

应用场景

前面分析了代理模式的结构与特点,现在来分析以下的应用场景:

  • 远程代理(Remote Proxy):这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。
    例如,用户访问网盘的虚拟硬盘时实际访问的是网盘空间。
  • 虚拟代理(Virtual Proxy):这种方式通常用于要创建的目标对象开销很大时。
    例如,下载一幅很大的图像需要很长时间,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
  • 保护代理(Protection Proxy):这种方式通常用于控制不同种类客户对真实对象的访问权限。
  • 智能指引(Smart Reference):主要用于调用目标对象时,代理附加一些额外的处理功能。

智能指引的典型用途包括:

  • 增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它;
  • 当第一次引用一个持久对象时,将它装入内存。
  • 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

扩展:动态代理模式

在前面介绍的代理模式中,代理类中包含了对真实主题的引用,这种方式存在两个缺点:

  • 真实主题与代理主题一一对应,增加真实主题也要增加代理
  • 设计代理以前真实主题必须事先存在,不太灵活。

采用动态代理模式可以解决以上问题(如 SpringAOP),C#中可以使用RealProxy实现动态代理,有两种方法:
第一种:只使用RealProxy,不能代理带out参数的方法(可能是我没找到),代码如下:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("***\r\n Begin program - logging with decorator\r\n");
        IRepository<Customer> customerRepository =RepositoryFactory.Create<Customer>();
        var customer = new Customer()
        {
            Id = 1,
            Name = "Customer 1",
            Address = "Address 1"
        };
        customerRepository.Add(customer);
        customerRepository.Update(customer);
        customerRepository.Delete(customer);
        Console.WriteLine("\r\nEnd program - logging with decorator\r\n***");
        Console.ReadLine();
    }
}
//客户类
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
}

//存储库接口
public interface IRepository<T>
{
    void Add(T entity);
    void Delete(T entity);
    void Update(T entity);
    IEnumerable<T> GetAll();
    T GetById(int id);
}

//真实储存库
public class Repository<T> : IRepository<T>
{
    public void Add(T entity)
    {
        Console.WriteLine("Adding {0}", entity);
    }
    public void Delete(T entity)
    {
        Console.WriteLine("Deleting {0}", entity);
    }
    public void Update(T entity)
    {
        Console.WriteLine("Updating {0}", entity);
    }
    public IEnumerable<T> GetAll()
    {
        Console.WriteLine("Getting entities");
        return null;
    }
    public T GetById(int id)
    {
        Console.WriteLine("Getting entity {0}", id);
        return default(T);
    }
} 

//动态代理
class DynamicProxy<T> : RealProxy
{
    private readonly T _decorated;
    public DynamicProxy(T decorated) : base(typeof(T))
    {
        _decorated = decorated;
    }
    private void Log(string msg, object arg = null)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(msg, arg);
        Console.ResetColor();
    }
    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        var methodInfo = methodCall.MethodBase as MethodInfo;
        Log("In Dynamic Proxy - Before executing '{0}'",methodCall.MethodName);
        try
        {
            var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
            Log("In Dynamic Proxy - After executing '{0}' ",methodCall.MethodName);
            return new ReturnMessage(result, null, 0,methodCall.LogicalCallContext, methodCall);
        }
        catch (Exception e)
        {
            Log(string.Format("In Dynamic Proxy- Exception {0} executing '{1}'", e),methodCall.MethodName);
            return new ReturnMessage(e, methodCall);
        }
    }
}

//存仓库过程,自动执行代理
public class RepositoryFactory
{
    public static IRepository<T> Create<T>()
    {
        var repository = new Repository<T>();
        var dynamicProxy = new DynamicProxy<IRepository<T>>(repository);
        return dynamicProxy.GetTransparentProxy() as IRepository<T>;
    }
}    

第二种:使用RealProxy、MarshalByRefObject,可以代理带out参数的方法,代码如下:

//访问类
public class Program
{
    static void Main(string[] args)
    {
        //动态代理模式        
        Proxy<ISubject> proxy = new Proxy<ISubject>(new RealSubject());
        ISubject subject = (ISubject)proxy.GetTransparentProxy();
        int arg = 0;
        subject.Request(out arg); 
        Console.WriteLine(arg);
        Console.ReadKey();
    }
}
//代理类
public class Proxy<T> : RealProxy where T: class
{    
    MarshalByRefObject myMarshalByRefObject;
    public Proxy(MarshalByRefObject realT) : base(typeof(T))
    {        
        myMarshalByRefObject = realT;
    }
    public override IMessage Invoke(IMessage myMessage)
    {        
        IMethodCallMessage myCallMessage = (IMethodCallMessage)myMessage;
        Console.WriteLine("动态代理方法中:执行前");
        IMethodReturnMessage myIMethodReturnMessage = RemotingServices.ExecuteMessage(myMarshalByRefObject, myCallMessage);
        Console.WriteLine("动态代理方法中:执行后");
        return myIMethodReturnMessage;
    }
}

//抽象主题
public interface ISubject
{
    void Request(out int arg);
}

//真实主题
public class RealSubject : MarshalByRefObject,ISubject
{
    public void Request(out int arg)
    {
        arg = 1;
        Console.WriteLine("访问真实主题方法...");       
    }
}

参考资料

C#中动态代理与泛型函数——CSDN
面向方面的编程-使用 RealProxy 类进行面向方面的编程——MSDN

posted @ 2020-08-26 14:55  二次元攻城狮  阅读(1190)  评论(0编辑  收藏  举报