读书活动感悟分享】特能扛——《设计模式:可复用面向对象软件的基础》第4章 结构型模式 - 4.7 PROXY(代理)_文章

读书活动感悟分享】特能扛——《设计模式:可复用面向对象软件的基础》第4章 结构型模式 - 4.7 PROXY(代理)

柳俊臣
发表于 2025-11-10 22:47:14

一、代理模式概述

代理模式(Proxy Pattern)是一种结构型设计模式,其核心思想是通过引入一个“代理对象”来控制对“目标对象”的访问。

在这种模式中,代理对象与目标对象实现相同的接口,客户端通过调用代理对象的方法间接访问目标对象。代理对象可以在调用目标对象的方法前后添加额外逻辑(如权限校验、日志记录、缓存处理等),而无需修改目标对象本身,从而实现对目标对象的增强或保护。

代理模式的核心角色包括:

  • 抽象主题(Subject):定义目标对象和代理对象的共同接口,确保代理对象可替代目标对象;
  • 目标对象(Real Subject):实现抽象主题的具体业务逻辑,是代理对象所代表的真实对象;
  • 代理对象(Proxy):实现抽象主题接口,内部持有目标对象的引用,在调用目标对象方法前后执行附加操作。



二、代理模式的使用场景

  • 远程代理:当目标对象位于远程服务器时,代理对象可封装网络通信细节(如序列化、Socket 连接),让客户端像访问本地对象一样访问远程对象(如 RPC 框架中的代理)。
  • 虚拟代理:当目标对象创建成本高(如大型图片、复杂报表),代理对象可先加载“占位内容”,延迟目标对象的初始化,直到真正需要时再创建(如图片加载时的“loading”占位符)。
  • 保护代理:控制对目标对象的访问权限,代理对象在调用目标方法前校验客户端权限,仅允许授权用户访问(如系统中敏感操作的权限控制)。
  • 日志代理:在调用目标对象方法前后记录日志(如入参、出参、执行时间),用于审计或调试,无需修改目标对象代码。
  • 缓存代理:对目标对象的计算结果进行缓存,重复请求时直接返回缓存数据,减少目标对象的重复计算(如数据库查询结果缓存)。
  • 三、代理模式的优缺点

    优点

  • 职责分离:目标对象专注于核心业务逻辑,代理对象负责附加功能(如日志、权限),符合“单一职责原则”。
  • 扩展性强:如需新增附加功能(如监控、限流),只需修改代理对象,无需改动目标对象,符合“开闭原则”。
  • 保护目标对象:通过代理控制访问,可隐藏目标对象的实现细节,或限制非法访问。
  • 灵活性高:客户端无需知道代理的存在,可透明切换目标对象或代理类型(如从日志代理切换为缓存代理)。
  • 缺点

  • 增加系统复杂度:引入代理对象会增加类的数量,降低代码的直接性(客户端需通过代理间接访问目标)。
  • 性能损耗:代理对象的额外操作(如日志、缓存)会增加请求处理的时间开销,尤其在高频调用场景下。
  • 代理逻辑可能冗余:若多个代理需实现相似附加功能(如统一日志格式),可能导致代码重复(需通过装饰器模式或 AOP 优化)。
  • 四、使用代理模式的注意事项

  • 避免过度代理:仅在需要附加功能(如权限、日志)时使用代理,简单场景下直接访问目标对象更高效。
  • 选择合适的代理类型:根据业务需求选择远程代理、虚拟代理等具体类型(如远程调用用远程代理,权限控制用保护代理)。
  • 确保接口一致性:代理对象必须与目标对象实现相同的接口,否则客户端无法透明替换,违背代理模式的设计初衷。
  • 处理代理链复杂度:若需多个代理叠加(如先权限校验,再日志记录,最后调用目标),需注意代理链的顺序,避免逻辑混乱(可通过工厂模式管理代理链)。
  • 警惕循环引用:代理对象持有目标对象的引用,需避免目标对象反向引用代理,导致内存泄漏。
  • 结合 AOP 技术:在复杂系统中,可通过 AOP(面向切面编程)自动生成代理(如 Spring 的动态代理),减少手动编写代理的冗余代码。
  • 五、示例代码

    using System;
    // 抽象主题接口
    public interface ISubject
    {
    void Request();
    }
    // 真实主题类(被代理对象)
    public class RealSubject : ISubject
    {
    public void Request()
    {
    Console.WriteLine("真实主题执行核心业务逻辑");
    }
    }
    // 代理类
    public class Proxy : ISubject
    {
    private readonly ISubject _realSubject;
    // 初始化时创建真实主题实例(也可通过构造函数注入)
    public Proxy()
    {
    _realSubject = new RealSubject();
    }
    public void Request()
    {
    // 代理前置处理
    PreRequest();

    // 调用真实主题的核心逻辑
    _realSubject.Request();

    // 代理后置处理
    PostRequest();
    }
    // 代理前置增强逻辑
    private void PreRequest()
    {
    Console.WriteLine("代理:执行前置处理(如权限验证、日志记录)");
    }
    // 代理后置增强逻辑
    private void PostRequest()
    {
    Console.WriteLine("代理:执行后置处理(如结果缓存、资源释放)");
    }
    }
    // 客户端调用示例
    public class Client
    {
    public static void Main()
    {
    // 客户端通过代理访问真实主题,无需直接依赖真实主题
    ISubject proxy = new Proxy();
    proxy.Request();
    }
    }
    六、代理模式在特来电的应用
    1.HSF(High Service Framework),特来电的三大核心框架之一。通过远程服务代理,实现不同模块之间的服务通信。

    2.服务降级组件。通过代理模式,实现依赖服务的多级降级功能。代码如下:

    //抽象代理类

    using System;
    using Teld.CCP.Foundation.Spi.MultiTargetContainer;
    namespace Teld.CCP.Foundation.Service.MultiTargetContainer.Middleware;

    public abstract class AbstractMiddlewareContainer : AbstractServiceContainer where T : class
    {
    public override AbstractProxy AddService(T service, IRouterFactory factory)
    {
    AbstractProxy abstractProxy = base.AddService(service, factory);
    MiddlewareProxy nextProxy = abstractProxy as MiddlewareProxy;
    if (base.ProxyServices.Count - 2 >= 0 && base.ProxyServices[base.ProxyServices.Count - 2] is MiddlewareProxy middlewareProxy)
    {
    middlewareProxy.SetNextProxy(nextProxy);
    }

    return abstractProxy;
    }

    public T1 ProxyInvoke(Func func)
    {
    AbstractProxy proxy = GetProxy();
    T arg = proxy as T;
    return func(arg);
    }
    }


    //实现类

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Teld.Boss.CSMSDK.Models;
    using Teld.CCP.Foundation.Service.MultiTargetContainer;
    using Teld.CCP.Foundation.Service.MultiTargetContainer.Middleware;

    namespace Teld.Boss.CSMSDK.Bill
    {
    public class BillStationQueryContainer : AbstractMiddlewareContainer
    {
    private static BillStationQueryContainer container;
    private readonly static object locker = new object();

    private BillStationQueryContainer()
    {
    AddService(new SGBillStationQuery(), null);
    AddService(new DBBillStationQuery(), null);
    }

    ///
    /// 获取实例
    ///
    ///
    public static BillStationQueryContainer GetInstance()
    {
    if (container == null)
    {
    lock (locker)
    {
    if (container == null)
    {
    container = new BillStationQueryContainer();
    }
    }
    }
    return container;
    }

    ///
    /// 创建代理
    ///
    ///
    ///
    public override AbstractProxy CreateProxy(IBillStationQuery service)
    {
    return new BillStationQueryProxy(service);
    }

    ///
    /// 查询互联互通电站信息
    ///
    ///
    ///
    public HlhtStationInfoVM GetHlhtStationInfo(HlhtStationQueryParms parms, string sqlMapName, CustomParam customParam = null)
    {
    var result = ProxyInvoke((P) =>
    {
    return P.GetHlhtStationInfo(parms, sqlMapName, customParam);
    });
    return result;
    }
    }
    }


    //SG调用实现

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Teld.Boss.CSMSDK.Models;
    using Teld.Core.ServiceGateway.Client;

    namespace Teld.Boss.CSMSDK.Bill
    {

    public class SGBillStationQuery : IBillStationQuery
    {

    /// 互联互通电站查询
    public HlhtStationInfoVM GetHlhtStationInfo(HlhtStationQueryParms parms, string sqlMapName, CustomParam customParam)
    {
    if (parms == null) return null;
    Dictionary filter = new Dictionary();
    filter.Add("filter", JsonConvert.SerializeObject(parms));
    var response = new SGHttpClient(true).PostMainIDC("CSM-GetHlhtStationInfo", filter, 3);
    return response.data;
    }
    }
    }


    //DB查询实现

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Teld.Boss.CSMSDK.DataAccess;
    using Teld.Boss.CSMSDK.Models;
    using Teld.Core.Kernel.Service;

    namespace Teld.Boss.CSMSDK.Bill
    {

    public class DBBillStationQuery : IBillStationQuery
    {
    /// 互联互通电站查询
    public HlhtStationInfoVM GetHlhtStationInfo(HlhtStationQueryParms parms, string sqlMapName, CustomParam customParam)
    {
    if (parms == null || string.IsNullOrWhiteSpace(sqlMapName)) return null;
    if (customParam == null) customParam = new CustomParam();
    var dao = DaoFactory.GetDao(sqlMapName);
    parms.StaTableName = customParam.StaTableName;
    parms.PileTableName = customParam.PileTableName;
    parms.StaExTableName = customParam.StaExTableName;
    return dao.GetHlhtStationInfo(parms);
    }

    }


    七、总结思考

    代理模式通过引入代理对象实现了对目标对象的间接访问,其核心价值在于在不修改目标对象的前提下,灵活添加附加功能,同时保证客户端对目标对象的透明访问。

    从实践角度看,代理模式的优势在于职责分离和扩展性——目标对象专注于业务逻辑,代理对象处理非业务逻辑(如日志、权限),当需求变化时(如新增缓存功能),只需新增代理类即可,无需改动原有代码。

    但需注意,代理模式会增加系统的复杂度和性能开销,因此不宜过度使用。在实际开发中,可结合具体场景选择代理类型(如远程调用用远程代理,权限控制用保护代理),并通过 AOP 技术(如动态代理)减少手动编写代理的冗余代码。

    总体而言,代理模式是解决“对象访问控制”和“功能增强”问题的有效方案,合理使用可显著提升系统的可维护性和灵活性。




    121 0

    评论
    

    意见反馈