结构型模式之代理模式

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

定义与特点

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

主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用

  • 代理对象可以扩展目标对象的功能

  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度

主要缺点有:

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

  • 增加了系统的复杂度

结构与实现

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

模式的结构

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

  • 抽象主题(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

(0)

相关推荐

  • 大话设计-代理模式

    记录大话设计学习过程. 代理模式:代理者为其他对象提供代理,以控制对真实对象的访问. 用户调用代理者,代理者通过真实的对象引用让对象去做事情.但是代理者可以附加一些功能,然后才让真实对象去做事情. 代 ...

  • 结构型设计模式 - 装饰者模式详解

    基本定义 装饰者模式属于结构型模式,它可以动态的将新功能附加到对象上,同时又不改变其结构.在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP). 模式结构 装饰者和被装饰者有相同 ...

  • 无废话设计模式(11)结构型模式--代理模式

    0-前言 代理模式定义:为其他对象提供一种代理以控制对这个对象的访问. 1-实现 1-1.简单UML图: 1-2.代码实现 //1.抽象父类 abstract class Actor { public ...

  • 设计模式-结构型模式总结

    结构型模式主要处理类或对象的组合,关注于如何将现有类或对象组织在一起形成更大的结构. 适配器模式 将一个类的接口转换成客户希望的另外一个接口,使原本不能一起工作的类可以一起工作. 适配器模式属于补偿机 ...

  • 结构型模式之组合模式

    在现实生活中,存在很多"部分-整体"的关系,例如,大学中的部门与学院.总公司中的部门与分公司.学习用品中的书与书包.生活用品中的衣月艮与衣柜以及厨房中的锅碗瓢盆等. 在软件开发中也 ...

  • 结构型模式之享元模式

    在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题.创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈.例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网 ...

  • 无废话设计模式(10)结构型模式--外观模式

    0-前言 外观模式定义:为子系统中的一组接口提供一个一致的界面,此模式定了一个高层接口    这一接口使得这一子系统更加容易使用: 1-实现 1-1.简单UML图: 1-2.代码实现 //1.子系统A ...

  • 无废话设计模式(9)结构型模式--享元模式

    0-前言 享元模式定义:运用共享技术有效地支持大量细粒度的对象. 1-实现 1-1.简单UML图:  1-2.代码实现 //1.抽象父类(网站父类) abstract class Website { ...

  • 每天学习一个设计模式(二):结构型之桥梁模式

    一.基本概念 桥梁模式(Bridge)是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥梁模式的用意是"将抽象化(Abstraction) ...

  • 结构型设计模式 - 组合模式详解

    基本介绍 1.组合模式(Composite Pattern)又叫部分整体模式,他创建了对象组的树形结构,将对象组合成树状结构以表示「整体 - 部分」的层次关系. 2.组合模式使得用户对单个对象和组合对 ...