• 二十三种设计模式[12]


    前言

           代理模式,属于对象结构型模式。在《设计模式 - 可复用的面向对象软件》一书中将之描述为“ 为其它对象提供一种代理以控制对这个对象的访问 ”。

           在代理模式中,通常使用一个类来代表另一个类的功能,并由这个代理对象去控制原对象的引用。

    结构

    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)

  • 相关阅读:
    黑马程序员_字符串常用处理方法
    动软代码生成器,主子表增加的时候子表的parentID无法插入问题解决方案
    大数据量高并发的数据库优化详解
    C# Socket网络编程精华篇 (转)
    html+javascript+soap获取webservice免费天气预报信息
    js中字符串怎么转化为日期
    attachEvent方法的作用
    C#中[WebMethod]的用法,aspx、ashx、asmx
    C#操作XML方法详解
    C#操作XML的通用方法总结
  • 原文地址:https://www.cnblogs.com/wxingchen/p/10078627.html
Copyright © 2020-2023  润新知