代码级:基于模版方法模式的复杂开闸逻辑业务解耦

发布时间:2026/10/27 10:20:42
代码级:基于模版方法模式的复杂开闸逻辑业务解耦 一、背景这是一个闸口监控页面。页面上有20多个闸口卡片数据包括车牌、货物、重量以及一排操作按钮空车开闸、重车开闸、人工开闸。不同闸口的差别很大- A类闸口分重车、空车两种开闸- B类闸口也分重车、空车但是参数不一样- C类闸口走通用的人工开闸- 入口闸口要先查司机信息查不到就走登记流程、查到才直接开闸开始的写法一种闸口方式一个方法weightCarOpenGate、emptyCarOpenGate、hclWeightCarOpenGate、hclEmptyCarOpenGate、artificial它们 80%的代码是一样的只有最后调用的开闸方法、按钮类型的key值不同二、整体流程1. 一种按钮一个方法粘贴复制问题修改一个闸口会修改 WeightCarOpenGate 和 EmptyCarOpenGate多个函数。// 同 hclWeightCarOpenGate、hclEmptyCarOpenGate、artificial、opened6 weightCarOpenGate(items) { if (items.truckno || items.truckno null) { return this.$message.error(请先输入车牌号码) } if (items.truckno.length 7) { return this.$message.error(请正确输入完整车牌) } if (!this.lockGateAction(ManualBtnState5)) return this.refreshCurrentTraceId() if (items.businessType 1 items.status ! 7) { getSearchCar({ carNum: items.truckno }).then(res { if (res.code 200) { this.northGateitems items this.registerSubmit(H) } else if (res.code 500) { this.opened68(items, 1) } }) } else { this.opened68(items, 1) } }2. 继续加 if/else问题虽然执行方法变短了分支从 5 个函数里搬到了一个函数里但是复杂度没降。每加一种闸口就要在 if/else 里找位置插入。3. 参数化把 ”最后调用的方法“ 变为参数问题1虽然减少了重复代码看似更干净。但是逻辑层和控制层的依赖倒置从逻辑层解耦控制层控制层决定怎么执行到逻辑层依赖控制层。问题2静态分析失效。从this.opened68()编译器看得见到动态字符串处理时编译器的跳转、重命名、引用查找全部失效this上只能取到属性名是变量 openerName 的值。如果有人修改方法名出现将编译期的错误拖到运行期。gateStatusOpen(items, { openerName weightOpened, showErrorTip false } {}) { // ...前面校验、抢锁、查司机不变 if (typeof this[openerName] function) { this[openerName](items) } else { console.error([gateStatusOpen] Method ${openerName} is not defined) this.$message.error(系统错误开闸失败请联系管理员) } }⚠️注意 参数化的错误不是“耦合变强”是“耦合变得不可见”4. 模版方法 和 回调注入把 ”最后调用的方法“ 变为回调参数openGateflow(items, { callSource, lockKey, open, registerOpenType }) { this.callSource callSource if (!this.validateTruck(items)) return if (!this.lockGateAction(lockKey)) return this.refreshCurrentTraceId() if (items.businessType 1 items.status ! 7) { getSearchCar({ carNum: items.truckno }).then(res { if (res.code 200) { this.northGateitems items this.registerform.carNum items.truckno this.registerform.gateName items.businessName this.registerSubmit(registerOpenType) } else if (res.code 500) { open(this.buildOpenForms(items), items) } }) } else { open(this.buildOpenForms(items), items) } }, weightCarOpenGate(items) { this.openGateflow(items, { callSource: 13weightCar, lockKey: ManualBtnState5, open: () this.opened68(items, 1), }) }, hclEmptyCarOpenGate(items){ this.openGateflow(items, { callSource: hclEmptyCar, lockKey: ManualBtnState8, registerOpenType: E, open: kind this.hclDoOpen( kind normal ? this.normalFrom : this.specialFrom, E, kind normal ? hcl_empty_normal : hcl_empty_special ), }) }⚠️注意这套骨架能稳定一个重要原因是业务状态有限、处理方式可枚举。每个 status 对应一个确定的决策而不是完整的动作即 状态驱动的路由表/决策。开闸类型的变化只替换最后一步不改变前面步骤的顺序。三、技术细节1. 什么情况下采用经典策略模式当前场景回调函数的参数不一样没有统一的契约无状态不需要运行时替换升级成类的判断标准有状态和依赖状态同一个 策略实例 被调用两次第二次要能看见第一次留下的记录即跨调用。EG同一辆车只允许一次在途开闸的场景。状态 Map对象车辆以及它的开闸请求pending:Map车牌, Promise 边界 策略实例状态 是按照 闸口 还是 全局这里是全局。依赖每个 策略实例 中存在各自需要长期持有的外部协作。EG每种开闸方式依赖不同的服务。服务场景配合审批服务、表单构建和接口。这些依赖作为参数每次都要修改。存在隐患“什么时候开闸” 和 “开闸需要哪些服务” 职责容易混乱。所以把依赖在创建策略时一次性注入更合适。⚠️注意组件层和策略层之间的职责边界或者说 状态所有权边界“在途”术语车辆未正常出场或者系统没有把车辆上一条入场、通行记录结束之前不能再次触发开闸。四、总结通过对比5段高度重合的代码分析出固定骨架校验数据、按钮锁、刷traceId、判断出入口和白名单、查司机、组装参数、执行编排的统一入口函数openGateflow 作为模版方法。这里是一种模版方法的思想因为经典的模版方法是依靠继承本例中使用的是高阶函数。根据策略模式一系列可互换对象实现同一个接口思想使用回调注入进行轻量替代。回调注入方式的选择首先 提取他们做的同一件事请 -用哪种方式开闸其次选择装载并执行这件事的载体 -函数最后接入业务逻辑。通俗来说结账流程固定但付款方式由顾客选openGateflow 是结账流程open 回调是付款方式策略模式则是给每种付款方式各做一个 POS 插件。最终实现把这套顺序收进openGateflow作为固定流程把唯一变化的一步——“用哪种方式开闸”——做成函数参数由调用方注入的重构效果script 代码从 748 行到 172 行开发速度从1小时提升到15分钟。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询