【读书活动感悟分享】特能扛——《设计模式:可复用面向对象软件的基础》第5章 行为模式 - 5.1 职责链】_文章

【读书活动感悟分享】特能扛——《设计模式:可复用面向对象软件的基础》第5章 行为模式 - 5.1 职责链】

张建利
发表于 2025-11-11 16:13:41

一、概述定义:责任链模式的架构本质与核心组件

责任链模式并非简单的 “流程串联”,而是通过 “定义链路契约、划分职责边界、动态传递请求”,解决工业级系统中 “请求上下游强耦合、流程调整成本高、职责划分不清晰” 三大痛点。其架构本质是 “将请求的‘发送权’与‘处理权’分离,让多个处理节点形成链式结构,按序协作完成请求处理”。
责任链模式是一种行为型设计模式,其核心逻辑可概括为:构建由多个 “处理者” 组成的链式结构,每个处理者持有下一个处理者的引用;当请求进入链路后,处理者会先判断自身是否能处理该请求 —— 能处理则执行对应逻辑,不能处理则将请求传递给下一个处理者,直至请求被处理或链路终止。
这种设计的关键价值在于 “解耦”:请求的发送者无需知道具体由哪个处理者负责,只需将请求提交给链路入口;处理者也无需关心请求的来源,只需专注于自身职责范围内的逻辑。例如在充电桩的用户充电请求中,发送请求的用户无需知道 “权限校验、设备状态判断、账户信息获取” 分别由哪个模块处理,只需发起充电请求即可。
二、应用场景
责任链模式在充电桩行业并非 “理论工具”,而是解决多个核心业务场景的 “架构利器”。
1、通用场景特征
请求处理者不确定场景
多个对象均可处理同一类请求,但运行时需根据请求内容、上下文或系统状态动态确定具体处理对象(如不同权限的审批者、不同级别的日志处理器)。
 流程动态组合场景
需要在运行时灵活调整请求的处理流程,支持动态新增、移除或调整处理节点的顺序。
解耦请求发送与接收场景
需避免请求发起者与处理者之间的直接耦合,使发起者无需知晓具体处理逻辑的实现细节。
2、“启动充电指令下发” 的链路实践
本次重构的 “启动充电指令下发” 业务,是责任链模式在充电桩行业的典型落地场景。原业务采用 “线性硬编码”,新增步骤需修改原有代码,异常处理逻辑混乱;重构后通过责任链模式拆分 6 个核心模块,完全迁移至 SG 层实现,链路结构如下:
链路节点拆解与架构职责
1. 模块一:权限校验处理器
架构职责:作为链路入口,校验用户是否拥有该终端电站的使用权限;
关键优化:复用原有 “带权限的终端状态查询” 封装,统一权限校验逻辑,避免重复开发;

链路控制:权限校验失败则直接返回 “无权限” 错误,终止链路;校验成功则传递请求至 “设备状态判断处理器”。
2. 模块二:设备状态判断处理器

架构职责:判断充电桩是否处于 “空闲可充电” 状态,是启动充电的核心前置条件;
关键优化:
去除对 VRDC 接口的依赖,减少外部接口调用链路;
实现 “降级逻辑”:调用 DRDC 接口失败时,兼容使用本地终端状态缓存判断;
链路控制:设备状态异常(如忙、故障)则返回错误;状态正常或降级判断通过则传递请求至 “账户信息处理器”。

3. 模块三:账户信息处理器
架构职责:确保用户账户信息存在且有效,为后续 “定制业务判断”“CM 接口调用” 提供数据支撑;
关键优化:通过 “主数据订阅 < 客户信息表>”,减少对 “账户查询接口” 的实时调用,提升链路性能;
 链路控制:账户不存在则自动触发注册逻辑,完成后传递请求;账户异常则返回错误。
4. 模块四:定制业务判断处理器
 架构职责:处理充电桩行业的个性化业务规则,如 “车枪识别充电是否开启”“用户是否已完成爱车认证”;
 链路控制:定制规则不满足则返回错误(如未完成爱车认证则拒绝启动);满足则传递请求至 “CM 接口调用处理器”。
5. 模块五:CM 接口调用处理器
 架构职责:调用充电数据中心(CM)的核心接口,发起实际的 “启动充电” 指令,是链路的 “核心执行节点”;
关键优化:
对接最新降级组件,针对充电数据中心接口实现 “故障降级”(如接口超时则重试 1 次,重试失败则返回降级提示);
主数据中心接口默认正常请求,保证核心流程稳定性;
链路控制:接口调用成功则传递请求至 “异步结果处理处理器”;调用失败(不可降级)则直接返回错误。
6. 模块六:异步结果处理处理器
架构职责:处理 CM 接口返回的异步结果,包括 “推送启动充电结果给用户”“上报监控数据至运维平台”“记录流程日志至数据库”;
链路控制:作为链路末尾节点,无下一个处理者,处理完成后返回最终结果给客户端。
三、优缺点:责任链模式的架构价值与工业级风险规避
在选择职责链模式时,需要清醒地认识到其带来的架构收益与潜在代价,通过合理的设计规避风险,最大化模式的价值。
1、核心优势:架构层面的 “解耦与扩展” 价值​
解耦请求发送者与接收者​
客户端仅需将请求发送至链路首个节点,无需知晓后续处理节点与流转路径,彻底打破 “发送者 - 接收者” 直接依赖​
处理节点变更(新增、删除、替换)时,客户端代码无需修改,降低系统维护成本​
支持动态调整处理流程​
可在运行时灵活调整链路结构(增删节点、调整节点顺序)​
适配业务流程频繁变更场景(如临时新增 / 移除审批环节),无需重构整体流程逻辑,提升系统适应性​
符合单一职责与开闭原则​
每个处理器节点职责单一明确,仅处理自身范围内业务逻辑,降低代码复杂度与维护难度​
新增处理节点时,无需修改现有代码,只需实现抽象处理器接口并接入链路,符合 “开闭原则”,支撑系统长期扩展​
减少冗余开发,提升代码复用率​
2、潜在缺陷:架构设计中需规避的 “风险点”​
请求处理的 “不确定性” 与 “断链风险”​
风险:链路设计不完整(缺兜底处理器)会导致请求无人处理、直接 “丢失”;节点逻辑漏洞(未正确传递请求)会引发 “断链”,中断流程​
规避方案:链路末尾必须添加 “默认处理器”(如返回统一错误提示),并通过严格测试确保链路完整性​
链路过长导致的 “性能损耗”​
四、实现示例
抽象处理器(Handler):
处理器的抽象类,声明处理请求的抽象接口,并持有对下一个处理器的引用。


    /// 
    /// 上下文抽象类
    /// 
    [Serializable]
    public abstract class AbstractContext
    {
        /// 
        /// 基类的构造函数
        /// 
        public AbstractContext()
        {
            ContextId = Guid.NewGuid().ToString();
            IsContinue = true;
        }

        /// 
        /// 上下文ID
        /// 
        public string ContextId { get; set; }

        /// 
        /// 是否继续执行
        /// 
        public bool IsContinue { get; set; }
    }
        /// 
    /// 启动充电 上下文
    /// 
    internal class QueryStartChargeContext : AbstractContext
    {
        /// 
        /// 构造
        /// 
        public QueryStartChargeContext() : base()
        {
        }

        /// 
        /// 参数
        /// 
        public QueryChargeParams @params { get; set; }
        /// 
        /// 返回值
        /// 
        public StartChargeResult @result { get; set; } = new StartChargeResult();

        /// 
        /// 终端信息
        /// 
        public LocalCachePileAndStationModel pileDetail { get; set; }

        /// 
        /// 账户注册信息
        /// 
        public Teld.OIC.StandardModels.RegistResult registInfo { get; set; }

        /// 
        /// 请求Set充电参数
        /// 
        public OICStartChargingParameter OICcmParam { get; set; }

        /// 
        /// 请求主数据中心充电参数
        /// 
        public StartChargingParameter cmParam { get; set; }

        /// 
        /// 车辆信息
        /// 
        public VehicleInformationVM carinfo { get; set; }

        /// 
        /// 二级来源
        /// 
        public string SecondSource { get; set; }
    }

具体处理器(ConcreteHandler):
继承自抽象处理器,包含了对处理请求接口的具体实现,负责处理特定类型的请求或将请求传递给下一个处理器。


    /// 
    /// 查询终端电站信息并判断权限
    /// 
    internal class GetPileDetailProcess : AbstractSyncMiddlware
    {
        public GetPileDetailProcess(AbstractSyncMiddlware next) : base(next)
        {
        }
        public override void Before(QueryStartChargeContext context)
        {
            //TODO: 业务逻辑
        }
        public override void Execute(QueryStartChargeContext context)
        {
            StartChargeResult outResult = new StartChargeResult();
            outResult.StartChargeSeq = context.@params.StartChargeSeq;
            outResult.StartChargeSeqStat = StartChargeSeqStat.StartUp;
            outResult.ConnectorID = context.@params.ConnectorID;
            outResult.SuccStat = SuccStat.Success;
            outResult.FailReason = StopChargeErrorCode.Success;
            var pileDetail = OICPileCacheMgr.GetPileInfoByPileID(context.@params.Merchant, context.@params.ConnectorID);
            if (pileDetail == null)
            {
                outResult.StartChargeSeqStat = StartChargeSeqStat.Unknown;
                outResult.SuccStat = SuccStat.Fail;
                outResult.FailReason = StartChargeErrorCode.NoPermission;
            }
            else
            {
                context.pileDetail = pileDetail;
            }
            context.@result = outResult;
        }
        public override void After(QueryStartChargeContext context)
        {
            //TODO: 业务逻辑
        }

    }
        /// 
    /// 检查充电来源
    /// 
    internal class CheckSecondSourceProcess : AbstractSyncMiddlware
    {
        public CheckSecondSourceProcess(AbstractSyncMiddlware next) : base(next)
        {
        }
        public override void Before(QueryStartChargeContext context)
        {
            //TODO: 业务逻辑
        }
        public override void Execute(QueryStartChargeContext context)
        {
            if (context.@result.SuccStat == SuccStat.Success)
            {
                try
                {
                    var codeItems = CodeItemsCacheMgr.GetSecondSourceFromMultiCacheBuilder(context.@params.Merchant.Code);
                    if (codeItems == null  )
                    {
                        context.@result.StartChargeSeqStat = StartChargeSeqStat.Unknown;
                        context.@result.SuccStat = SuccStat.Fail;
                        context.@result.FailReason = StartChargeErrorCode.NoSecondSourceErr;
                    }
                    else
                    {
                        context.SecondSource = codeItems.Code;
                    }
                }
                catch (Exception ex)
                {
                    context.@result.StartChargeSeqStat = StartChargeSeqStat.Unknown;
                    context.@result.SuccStat = SuccStat.Fail;
                    context.@result.FailReason = StartChargeErrorCode.NoSecondSourceErr;
                    Common.NewLogHelper.Instance.Error("码表服务调用IPubBusinessService的GetCodeItemBySetIds服务异常", ex);
                }
            }
        }
        public override void After(QueryStartChargeContext context)
        {
            //TODO: 业务逻辑
        }

    }

客户端(Client):
负责创建处理器的实例,并将它们加入到职责链中。然后向第一个处理器发送请求,并等待职责链的返回结果。


    public sealed class StartChargeBusiness
    {
        public StartChargeResult StartCharge(QueryChargeParams param)
        {
            QueryStartChargeContext context = new QueryStartChargeContext();
            context.@params = param;
            try
            {
                var builder = SyncMiddlwareBuilder.Create();
                builder
                    .Use((next) => new GetPileDetailProcess(next))
                    .Use((next) => new CheckSecondSourceProcess(next))
                    .Use((next) => new CheckPileStatusProcess(next))
                    .Use((next) => new AccountRegistProcess(next))
                    .Use((next) => new CheckVehicleInfoProcess(next))
                    .Use((next) => new StartForecastProcess(next))
                    .Use((next) => new OrgStartChargeParamProcess(next))
                    .Use((next) => new StartChargeProcess(next)); 
                //同步
                builder.Build().Invoke(context);
            }
            catch (Exception e)
            {
                Common.NewLogHelper.Instance.Error(e.Message, e);
                throw new OICException(OICExpType.CMServiceErr, e);
            }
            return context.result;
        }
    }

五、最终感悟
设计模式的价值并非 “炫技”,而是 “解决实际问题”。责任链模式通过 “解耦、扩展、分级” 的设计思想,不仅解决了 “启动充电指令下发” 的业务痛点,更沉淀了一套 “复杂流程架构方法论”。未来,随着充电桩行业向 “智能化、平台化” 演进,责任链模式将继续发挥其 “柔性流程” 的核心优势,成为连接 “业务需求” 与 “技术实现” 的关键桥梁 —— 而架构师的核心职责,就是让设计模式真正服务于业务,而非成为技术的 “枷锁”。

80 0

评论


意见反馈