ProxyPattern-代理模式

ZHIZRL / 2023-08-21 / 原文

在C#中,代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过创建一个代理对象来控制对其他对象的访问。代理对象充当着客户端和被代理对象之间的中间层,可以在访问对象时添加额外的功能,例如权限验证、延迟加载等。

代理模式有以下几个关键角色:

Subject(主题):定义了代理对象和真实对象的共同接口,客户端通过该接口访问真实对象。

RealSubject(真实主题):是代理模式中被代理的对象。它实现了Subject接口,并定义了真实对象的业务逻辑。

Proxy(代理):实现了Subject接口,并持有一个真实主题的引用。它可以代理客户端的请求,在调用真实主题前后执行额外的逻辑。

namespace ProxyPattern_代理模式
{
    /// <summary>
    /// 业务接口
    /// </summary>
    public interface ISubject
    {
        List<string> GetSomethingLong();
        void DoSomethingLong();
    }
}
namespace ProxyPattern_代理模式
{
    /// <summary>
    /// 具体业务
    /// </summary>
    public class RealSubject: ISubject
    {
        public RealSubject()
        {
            Thread.Sleep(2000);
            Console.WriteLine("RealSubject被构造。。。");
        }

        public void DoSomethingLong()
        {
            Thread.Sleep(1000);
            Console.WriteLine("DoSomethingLong");
        }

        public List<string> GetSomethingLong()
        {
            Thread.Sleep(1000);
            Console.WriteLine("GetSomethingLong");
            return new List<string>() { "123", "456", "789" };
        }
    }
}
namespace ProxyPattern_代理模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            {
                ISubject subject = new RealSubject();
                subject.GetSomethingLong();
                subject.DoSomethingLong();
                //输出结果
                //RealSubject被构造。。。
                //GetSomethingLong
                //DoSomethingLong
            }
        }
    }
}

当我们需要新增运行日志或者额外代码逻辑时,可以使用代理模式增加此功能,符合开闭原则

namespace ProxyPattern_代理模式
{
    public class ProxySubject : ISubject
    {
        private ISubject _iSubject = new RealSubject();
        public void DoSomethingLong()
        {
            Console.WriteLine("prepare DoSomethingLong");
            _iSubject.DoSomethingLong();
        }

        public List<string> GetSomethingLong()
        {
            Console.WriteLine("prepare GetSomethingLong");
            return _iSubject.GetSomethingLong();
        }
    }
}
namespace ProxyPattern_代理模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            {
                ISubject subject = new RealSubject();
                subject.GetSomethingLong();
                subject.DoSomethingLong();
                //输出结果
                //RealSubject被构造。。。
                //GetSomethingLong
                //DoSomethingLong
            }
            {
                ISubject subject = new ProxySubject();
                subject.GetSomethingLong();
                subject.DoSomethingLong();
                //输出结果
                //RealSubject被构造。。。
                //prepare GetSomethingLong
                //GetSomethingLong
                //prepare DoSomethingLong
                //DoSomethingLong
            }
            {

            }
            Console.Read();
        }
    }
}

异常代理,在代理类中增加try-catch异常处理

namespace ProxyPattern_代理模式
{
    public class ProxySubject : ISubject
    {
        private ISubject _iSubject = new RealSubject();
        public void DoSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare DoSomethingLong");
                _iSubject.DoSomethingLong();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<string> GetSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare GetSomethingLong");
                List<string> result = new List<string>();
                result = _iSubject.GetSomethingLong();

                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
    }
}

单例代理,将原本不具备单例的类使用代理模式增加单例。在实例化ISubject类时使用静态关键字static

namespace ProxyPattern_代理模式
{
    public class ProxySubject : ISubject
    {
        private static ISubject _iSubject = new RealSubject();
        public void DoSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare DoSomethingLong");
                _iSubject.DoSomethingLong();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<string> GetSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare GetSomethingLong");
                List<string> result = new List<string>();
                result = _iSubject.GetSomethingLong();

                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
    }
}

缓存代理,第一次请求得到的结果找个地方存起来,下次直接用,以节约时间。封装一个第三方缓存,代理查询时,优先缓存。

namespace ProxyPattern_代理模式
{
    public class CustomCache
    {
        /// <summary>
        /// 第三方存储---保证数据不丢失--可以放进来--可以获取---缓存
        /// </summary>
        private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();

        public static void Add(string key, object oValue)
        {
            CustomCacheDictionary.Add(key, oValue);
        }

        public static T GetT<T>(string key)
        {
            return (T)CustomCacheDictionary[key];
        }

        public static bool Exists(string key)
        {
            return CustomCacheDictionary.ContainsKey(key);
        }

    }
}
namespace ProxyPattern_代理模式
{
    public class ProxySubject : ISubject
    {
        private static ISubject _iSubject = new RealSubject();
        public void DoSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare DoSomethingLong");
                _iSubject.DoSomethingLong();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<string> GetSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare GetSomethingLong");
                string key = $"{nameof(ProxySubject)}_{nameof(GetSomethingLong)}";
                List<string> result = new List<string>();
                //在缓存中进行查询
                if (!CustomCache.Exists(key))
                {
                    result = _iSubject.GetSomethingLong();
                    CustomCache.Add(key, result);
                }
                else
                {
                    result = CustomCache.GetT<List<string>>(key);
                }

                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
    }
}

 延迟代理,在浏览网页时,通常优先加载客户能看的界面,看不到的可以先不加载,待客户滑动后再进行加载,以此达到节约资源和效率优化的目的。

    public class ProxySubject : ISubject
    {
        private static ISubject _iSubject = null;// new RealSubject();
        private void Init()
        {
            _iSubject = new RealSubject();//把一开始就构造对象  延迟到 调用Init才会初始化
        }
        public void DoSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare DoSomethingLong");
                //查询实例是否为空
                if (_iSubject == null)
                {
                    this.Init();
                }
                _iSubject.DoSomethingLong();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<string> GetSomethingLong()
        {
            try
            {
                Console.WriteLine("prepare GetSomethingLong");
                //查询实例是否为空
                if (_iSubject == null)
                {
                    this.Init();
                }
                string key = $"{nameof(ProxySubject)}_{nameof(GetSomethingLong)}";
                List<string> result = new List<string>();
                //在缓存中进行查询
                if (!CustomCache.Exists(key))
                {
                    result = _iSubject.GetSomethingLong();
                    CustomCache.Add(key, result);
                }
                else
                {
                    result = CustomCache.GetT<List<string>>(key);
                }

                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
    }
}

权限代理,在执行操作前需要查明操作人是否具有该操作权限时可以使用代理模式增加权限管控

namespace ProxyPattern_代理模式
{
    public class ProxySubject : ISubject
    {
        private static ISubject _iSubject = null;// new RealSubject();
        private void Init()
        {
            _iSubject = new RealSubject();//把一开始就构造对象  延迟到 调用Init才会初始化
        }
        public void DoSomethingLong()
        {
            try
            {
                //权限检查
                object user = CallContext.GetData("CurrentUser");
                if (user == null)
                {
                    throw new Exception("没有权限访问");
                }
                Console.WriteLine("prepare DoSomethingLong");
                //查询实例是否为空
                if (_iSubject == null)
                {
                    this.Init();
                }
                _iSubject.DoSomethingLong();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public List<string> GetSomethingLong()
        {
            try
            {
                //权限检查
                object user = CallContext.GetData("CurrentUser");
                if (user == null)
                {
                    throw new Exception("没有权限访问");
                }
                Console.WriteLine("prepare GetSomethingLong");
                //查询实例是否为空
                if (_iSubject == null)
                {
                    this.Init();
                }
                string key = $"{nameof(ProxySubject)}_{nameof(GetSomethingLong)}";
                List<string> result = new List<string>();
                //在缓存中进行查询
                if (!CustomCache.Exists(key))
                {
                    result = _iSubject.GetSomethingLong();
                    CustomCache.Add(key, result);
                }
                else
                {
                    result = CustomCache.GetT<List<string>>(key);
                }

                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }
    }
}
namespace ProxyPattern_代理模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            {
                //ISubject subject = new RealSubject();
                //subject.GetSomethingLong();
                //subject.DoSomethingLong();
                ////输出结果
                ////RealSubject被构造。。。
                ////GetSomethingLong
                ////DoSomethingLong
            }
            {
                //ISubject subject = new ProxySubject();
                //subject.GetSomethingLong();
                //subject.DoSomethingLong();
                ////输出结果
                ////RealSubject被构造。。。
                ////prepare GetSomethingLong
                ////GetSomethingLong
                ////prepare DoSomethingLong
                ////DoSomethingLong
            }
            {
                ISubject subject = new ProxySubject();
                subject.GetSomethingLong();
                CallContext.SetData("CurrentUser", "小明");
                subject.GetSomethingLong();
                //输出结果
                //没有权限访问
                //prepare GetSomethingLong
                //RealSubject被构造。。。
                //GetSomethingLong
            }
            Console.Read();
        }
    }
}

代理模式在需要控制对某个对象的访问时非常有用。通过代理对象,可以添加额外的逻辑或控制访问权限,而无需修改真实对象的代码。代理模式还可以实现延迟加载,在需要时才真正创建和初始化真实对象,提升系统性能。