设计模式——代理模式

代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。现实世界的代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角度看,似乎当事人并不存在,因为他只和代理人通信。而事实上代理人是要有当事人的授权,并且在核心问题上还需要请示当事人。
在软件设计中,使用代理模式的意图也很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节 (如 RMI),也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。
代理模式场景:
一、延迟加载
以一个简单的示例来阐述使用代理模式实现延迟加载的方法及其意义。假设某客户端软件有根据用户请求去数据库查询数据的功能。在查询数据前,需要获得数据库连接,软件开启时初始化系统的所有类,此时尝试获得数据库连接。当系统有大量的类似操作存在时 (比如 XML 解析等),所有这些初始化操作的叠加会使得系统的启动速度变得非常缓慢。为此,使用代理模式的代理类封装对数据库查询中的初始化操作,当系统启动时,初始化这个代理类,而非真实的数据库查询类,而代理类什么都没有做。因此,它的构造是相当迅速的。
在系统启动时,将消耗资源最多的方法都使用代理模式分离,可以加快系统的启动速度,减少用户的等待时间。而在用户真正做查询操作时再由代理类单独去加载真实的数据库查询类,完成用户的请求。这个过程就是使用代理模式实现了延迟加载。
延迟加载的核心思想是:如果当前并没有使用这个组件,则不需要真正地初始化它,使用一个代理对象替代它的原有的位置,只要在真正需要的时候才对它进行加载。使用代理模式的延迟加载是非常有意义的,首先,它可以在时间轴上分散系统压力,尤其在系统启动时,不必完成所有的初始化工作,从而加速启动时间;其次,对很多真实主题而言,在软件启动直到被关闭的整个过程中,可能根本不会被调用,初始化这些数据无疑是一种资源浪费。例如使用代理类封装数据库查询类后,系统的启动过程这个例子。若系统不使用代理模式,则在启动时就要初始化 DBQuery 对象,而使用代理模式后,启动时只需要初始化一个轻量级的对象 DBQueryProxy。
下面代码 IDbQuery 是主题接口,定义代理类和真实类需要对外提供的服务,定义了实现数据库查询的公共方法 request() 函数。DbQuery 是真实主题,负责实际的业务操作,DbQueryProxy 是 DbQuery 的代理类。

 public interface IDbQuery
    {
        string Request();
    }
 public class DbQuery : IDbQuery
    {
        public DbQuery()
        {
            try
            {
                Thread.Sleep(1000);//假设数据库连接等耗时操作
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        public string Request()
        {
            Console.WriteLine("我是真实类中的请求方法,当代理类发起请求我才实例化,别的不需要实例化");
            return "";
        }
    }
 public class DbQueryProxy : IDbQuery
    {
        private DbQuery _realDbQuery = null;
        public  string Request()
        {
            Console.WriteLine("我只是代理类中的请求方法,我只是被请求的时候才向真实类发起请求");
            //在真正需要的时候才能创建真实对象,创建过程可能很慢
            if (_realDbQuery == null)
            {
                _realDbQuery = new DbQuery();
            }//在多线程环境下,这里返回一个虚假类,类似于 Future 模式
            return _realDbQuery.Request();
        }

        public static void Main(String[] args)
        {
            DbQueryProxy q = new DbQueryProxy(); //使用代里
            q.Request(); //在真正使用时才创建真实对象
            Console.ReadLine();
        }
    }

二、动态代理
动态代理是指在运行时动态生成代理类。即代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。首先,不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护(如果我有一千个业务,那岂不是要编写一千个代理类,其实这种代理模式就是静态代理,它的缺点很明显,静态代理只能服务于一种类型的对象,不利于业务的扩展,那么我们就想了,能不能设计一个代理类可以服务于所有的业务对象呢?于是,这时候,动态代理就产生);其次,使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
下面代码是通过动态生成代理类来调用真实数据库查询的;具体代码如下:
生成动态代理类方法:

 /// <summary>
    /// 动态代理类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class DynamicProxy<T> : RealProxy
    {
        private readonly T _targetInstance = default(T);

        public Dictionary<string, DynamicAction> ProxyMethods { get; set; }

        public DynamicProxy(T targetInstance)
            : base(typeof(T))
        {
            _targetInstance = targetInstance;
        }
        public override IMessage Invoke(IMessage msg)
        {
            var reqMsg = msg as IMethodCallMessage;

            if (reqMsg == null)
            {
                return new ReturnMessage(new Exception("调用失败!"), null);
            }

            var target = _targetInstance as MarshalByRefObject;

            if (target == null)
            {
                return new ReturnMessage(new Exception("调用失败!请把目标对象 继承自 System.MarshalByRefObject"), reqMsg);
            }

            var methodName = reqMsg.MethodName;

            DynamicAction actions = null;

            if (ProxyMethods != null && ProxyMethods.ContainsKey(methodName))
            {
                actions = ProxyMethods[methodName];
            }

            if (actions != null && actions.BeforeAction != null)
            {
                actions.BeforeAction();
            }

            var result = RemotingServices.ExecuteMessage(target, reqMsg);

            if (actions != null && actions.AfterAction != null)
            {
                actions.AfterAction();
            }

            return result;
        }
    }

获取动态代理类的动态代理工厂:

/// <summary>
    /// 代理工厂
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ProxyFactory<T>
    {
        public static T Create(T obj, Dictionary<string, DynamicAction> proxyMethods = null)
        {
            var proxy = new DynamicProxy<T>(obj) { ProxyMethods = proxyMethods };

            return (T)proxy.GetTransparentProxy();
        }
    }

动态代理要执行的方法

   /// <summary>
    /// 动态代理要执行的方法
    /// </summary>
    public class DynamicAction
    {
        /// <summary>
        /// 执行目标方法前执行
        /// </summary>
        public Action BeforeAction { get; set; }


        /// <summary>
        /// 执行目标方法后执行
        /// </summary>
        public Action AfterAction { get; set; }


    }

后台真实需要调用的方法:

  public class DbQuery : ContextBoundObject
    {
        public DbQuery()
        {
            try
            {
                Thread.Sleep(1000);//假设数据库连接等耗时操作
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        public string Request()
        {
            Console.WriteLine("我是真实类中的请求方法,当代理类发起请求我才实例化,别的不需要实例化");
            return "";
        }
    }

调用:

public static void Main(String[] args)
       {

            var proxyMotheds = new Dictionary<string, DynamicAction>();

            // key is  Proxy's methodName, value is Actions
            proxyMotheds.Add("Request", new DynamicAction()
            {
                BeforeAction = new Action(() => Console.WriteLine("Before Doing....")),
                AfterAction = new Action(() => Console.WriteLine("After Doing...."))
            });

            var user = new DbQuery();
            //proxy for User
            var t = ProxyFactory<DbQuery>.Create(user, proxyMotheds);
           t.Request();
            Console.ReadLine();
       }

运行结果:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值