模板方法模式:设备软件里「流程骨架固定、细节各自填」怎么写
这篇解决一个问题设备软件里很多模块的流程骨架一样启动→运行→停止→回零→结批但每一步的具体实现不同。怎么写才不让骨架代码复制粘贴也不让子类从头写起。从最直觉的写法开始一步步讲清模板方法模式解决什么。一、先从一个生活例子说起你开一家奶茶店做奶茶的流程是固定的烧水。放茶叶。泡茶。加奶。加糖。封杯。这个流程是「祖传秘方」每个店员都要按这个步骤来不能乱序。但每一步的具体做法可以不同红茶用 100 度水绿茶用 80 度全脂奶还是脱脂奶加糖还是不加糖。老板的做法是把流程步骤写死在墙上骨架但每一步用什么茶叶、什么奶、什么糖让店员自己定细节。店员不用从头设计流程只要按墙上的步骤填自己的材料就行。这就是模板方法。模板方法的本质父类定义流程骨架先做什么再做什么子类填每一步的具体实现。骨架不变细节可变。二、回到代码不用模板方法会怎样设备软件里每个 Actor 都有生命周期启动、运行、停止、回零、结批。流程骨架一样但每个模块的具体操作不同。最直觉的写法每个子类从头写class Arm { public: void Run() { m_bRunFlag true; Log(手臂启动); CreateThread(); } void Stop() { m_bRunFlag false; Log(手臂停止); } void Home() { m_bHomeFlag true; Log(手臂回零); CreateHomeThread(); } void EndLot() { m_bEndingLot true; Log(手臂结批); } void WorkFlow() { /* 手臂特有的状态机 */ } void HomeFlow() { /* 手臂特有的回零流程 */ } }; class Channel { public: void Run() { m_bRunFlag true; Log(料道启动); CreateThread(); } void Stop() { m_bRunFlag false; Log(料道停止); } void Home() { m_bHomeFlag true; Log(料道回零); CreateHomeThread(); } void EndLot() { m_bEndingLot true; Log(料道结批); } void WorkFlow() { /* 料道特有的状态机 */ } void HomeFlow() { /* 料道特有的回零流程 */ } }; class Buffer { // ... 又是几乎一样的 Run/Stop/Home/EndLot };问题一堆问题一重复代码。Run、Stop、Home、EndLot的逻辑在每个类里几乎一样只是Log里的名字不同。十个模块就是十份重复。问题二改骨架要改所有类。 有一天Run里要加「检查安全门」十个类都要加。漏一个就出 bug。问题三流程顺序没保障。 每个类自己写Run有的先CreateThread再设标志有的反过来。顺序不一致行为可能不同。问题四新人不知道流程该包含什么。 新写一个模块要参考老模块抄一遍容易漏步骤。根本问题流程骨架和具体实现搅在一起没有分离。三、模板方法模式怎么解决思路把流程骨架提到基类写成普通函数非虚函数骨架里调虚函数钩子子类只填虚函数的实现。第一步基类定义骨架class Actor { bool m_bRunFlag false; bool m_bHomeFlag false; bool m_bEndingLot false; bool m_bThreadExist false; public: // 骨架固定流程不可被子类覆盖 void Run() { if (!m_bRunFlag) { m_bRunFlag true; Log(Run); OnRun(); // 钩子子类可以填 CreateThread(); } } void Stop() { if (m_bRunFlag || m_bHomeFlag) { Log(Stop); m_bRunFlag false; m_bHomeFlag false; OnStop(); // 钩子子类可以填 } } void Home() { if (!m_bHomeFlag) { m_bHomeFlag true; Log(Home); OnHome(); // 钩子子类可以填 CreateHomeThread(); } } void EndLot() { if (!m_bEndingLot) { m_bEndingLot true; Log(EndLot); OnEndLot(); // 钩子子类可以填 } } // 钩子子类按需填不填就用默认空实现 protected: virtual void OnRun() {} // 启动时的额外操作 virtual void OnStop() {} // 停止时的额外操作 virtual void OnHome() {} // 回零前的额外操作 virtual void OnEndLot() {} // 结批时的额外操作 // 纯虚钩子子类必须实现 public: virtual void WorkFlow() 0; // 工作流程每个模块必须写 virtual void HomeFlow() 0; // 回零流程每个模块必须写 virtual bool HomeData() 0; // 数据复位每个模块必须写 };第二步子类只填细节class Arm : public Actor { protected: void OnRun() override { Log(手臂额外启动检查); CheckSafeDoor(); } void OnStop() override { Log(手臂额外停止操作); ArmZToSafe(); // 手臂停止时抬 Z } public: void WorkFlow() override { // 手臂特有的状态机 while (true) { Sleep(1); if (!IsRun()) continue; switch (m_step) { ... } } } void HomeFlow() override { // 手臂特有的回零流程 HomeZ(); HomeX(); } bool HomeData() override { m_step.Reset(); m_state.Reset(); return true; } };class Channel : public Actor { protected: void OnStop() override { Log(料道额外停止操作); CloseDoor(); // 料道停止时关门 } public: void WorkFlow() override { // 料道特有的状态机 } void HomeFlow() override { // 料道特有的回零流程 } bool HomeData() override { ResetTrayCount(); return true; } };第三步调用方只调骨架// 总控一键启动 Actor::RunAllActors(); // 内部调每个 Actor 的 Run() // Run() 是骨架自动走完流程子类的 OnRun() 被调 // 总控一键停止 Actor::StopAllActors(); // 内部调每个 Actor 的 Stop() // Stop() 是骨架自动走完流程子类的 OnStop() 被调关键变化Run/Stop/Home/EndLot的骨架只在基类写一次。子类只填OnRun/OnStop/WorkFlow/HomeFlow等钩子。加新模块只填钩子不抄骨架。改骨架比如Run里加安全检查只改基类所有子类自动生效。四、模板方法模式的标准结构角色例子AbstractClass抽象类Actor定义骨架方法和钩子ConcreteClass具体类Arm/Channel/Buffer填钩子实现Template Method模板方法Run()/Stop()/Home()骨架固定不可覆盖Hook钩子OnRun()/OnStop()子类按需填AbstractClassActor │ Run() { 设标志 → OnRun() → CreateThread() } 骨架固定 │ ├──→ ConcreteClassArm │ OnRun() { 检查安全门 } │ WorkFlow() { 手臂状态机 } │ └──→ ConcreteClassChannel OnRun() {} 不需要额外操作空实现 WorkFlow() { 料道状态机 }核心骨架是「非虚函数」子类不能覆盖。钩子是「虚函数」子类按需覆盖。这保证了流程顺序固定细节可变。五、模板方法模式解决了什么问题不用模板方法用模板方法重复代码每个子类抄一遍骨架骨架在基类写一次改骨架改所有子类只改基类流程顺序每个子类自己定可能不一致基类固定保证一致新人上手参考老模块抄容易漏继承基类填钩子不漏强制实现靠人记得写 WorkFlow纯虚函数不写编译不过一句话模板方法把「流程骨架」和「步骤细节」分开骨架不变、细节可变。六、设备软件里的真实场景场景一Actor 生命周期——最典型的模板方法前面讲的Run/Stop/Home/EndLot就是真实场景。每个 Actor 的生命周期骨架一样// 骨架基类 void Run() { 设运行标志; Log(Run); OnRun(); // 钩子子类填额外启动操作 CreateThread(); // 创建线程跑 WorkFlow } // 子类填钩子 void Arm::OnRun() { CheckSafeDoor(); } void Channel::OnRun() { } // 料道不需要额外操作场景二回零流程回零的骨架是固定的回零前准备 → 各轴回零 → 等待完成 → 回零后处理。// 骨架 void HomeFlow() { Log(开始回零); if (!HomeData()) // 钩子1数据复位 { Alarm(回零数据复位失败); return; } DoHome(); // 钩子2各轴回零 WaitHomeComplete(); // 骨架等所有轴回零完成 OnHomeDone(); // 钩子3回零后处理 Log(回零完成); } // 手臂填 void Arm::DoHome() { HomeZ(); HomeX(); } void Arm::OnHomeDone() { ArmToSafe(); } // 料道填 void Channel::DoHome() { HomeConveyor(); } void Channel::OnHomeDone() { CloseDoor(); }骨架保证回零顺序固定先复位→再回零→再等→再后处理每个模块只填「回零哪些轴」「回零后干什么」。场景三取放动作手臂的取料和放料也有固定骨架// 取料骨架 RunRetArmError PickStation(Station* st, Pos pickPos, Pos stPos) { // 1. 移动到站点上方 auto ret MotionToStation(st, pickPos, stPos, ArmMotion::Pick); if (!ret.IsOK()) return ret; // 2. 真空检测钩子不同手臂可能不同 auto airRet CheckArmPickerAir(); if (!airRet.IsOk()) return airRet; // 3. 更新站点信息钩子不同模式更新不同 UpdateInfoFromStation(st, pickPos, stPos); // 4. UI 刷新 UpdateUI(Normal); return airRet; }CheckArmPickerAir和UpdateInfoFromStation可以是虚函数BIB 手臂可能覆盖CheckArmPickerAir加叠料检测Sorting 手臂可能覆盖UpdateInfoFromStation加 Bin 信息。骨架不变细节可变。场景四结批流程结批的骨架也是固定的标记结批 → 等当前任务完成 → 通知下游 → 设结批完成。// 骨架 void EndLot() { m_bEndingLot true; Log(开始结批); OnEndLot(); // 钩子子类填额外结批操作 } // 等结批完成 bool WaitEndLot() { while (!m_bEndedLot) { Sleep(100); if (!IsExistThread()) return false; } OnEndLotDone(); // 钩子子类填结批完成后的清理 return true; }七、模板方法 vs 策略别搞混这两个都涉及「骨架固定、细节可变」容易混。模板方法策略模式谁定骨架父类定骨架Context 不定骨架策略定整个算法怎么变细节子类覆盖钩子换策略对象关系继承is-a组合has-a粒度流程内部的每一步整个算法适合生命周期流程固定整套算法可替换例子Run/Stop/Home 骨架TransportStrategy 整套选站算法简单记模板方法是「骨架不变、填空题」策略是「整张卷子换一份」。模板方法用继承策略用组合。八、模板方法的坑坑一钩子太多子类不知道该填哪个class Actor { protected: virtual void OnRun() {} virtual void OnRunBefore() {} virtual void OnRunAfter() {} virtual void OnRunCheck() {} virtual void OnRunPrepare() {} // ... 十个钩子 };子类一看这么多钩子不知道哪些必须填、哪些可选填。对策钩子要少而精。只有「确实需要子类定制」的地方才开钩子。不需要定制的步骤直接在骨架里写死。坑二子类覆盖了骨架方法class Arm : public Actor { public: void Run() override // 覆盖了骨架 { // 自己写一套基类骨架被绕过 } };如果骨架方法是虚函数子类可能不小心覆盖它导致流程顺序被破坏。对策骨架方法设为非虚函数不加 virtual或者加final关键字禁止覆盖。void Run() final // 子类不能覆盖 { // 骨架 }坑三骨架里调子类钩子时机不对void Run() { CreateThread(); // 先创建了线程 OnRun(); // 再调钩子——但线程已经开始跑了钩子来不及 }钩子的调用时机要放在「正确的步骤之间」。放在线程创建之后子类的OnRun可能来不及生效。对策仔细设计骨架里钩子的位置。OnRun放在设标志之后、创建线程之前。坑四骨架太长变成巨型函数void Run() { // 30 行骨架逻辑 // 5 个钩子调用 // 又 30 行 }骨架太长说明流程太复杂不适合一个模板方法管。对策拆成多个小模板方法。Run调PrepareRunDoRunPostRun每个小方法有自己的钩子。九、可复用结论模板方法模式的本质父类定义流程骨架非虚函数子类填每一步细节虚函数钩子。骨架不变细节可变。两个角色AbstractClass骨架钩子、ConcreteClass填钩子。设备软件最典型应用Actor 的Run/Stop/Home/EndLot生命周期、回零流程、取放动作、结批流程。核心价值骨架写一次不重复改骨架只改基类流程顺序有保证新模块只填钩子不漏步骤。和策略的区别模板方法用继承填钩子骨架不变策略用组合换对象整个算法换。坑钩子要少而精、骨架方法加 final 防覆盖、钩子调用时机要正确、骨架太长就拆。模板方法模式在设备软件里不是「课本上的 Template Method」而是「所有 Actor 的生命周期怎么统一管」的实打实解法。用对了加新模块只填几个钩子骨架自动跑完用错了要么每个子类抄一遍骨架改不动要么钩子太多子类不知道填哪个。关键是分清「骨架该固定的」和「细节该开放的」在正确的地方开钩子在其余地方锁死。

相关新闻

Python+Django+Vue构建智能招聘分析系统

Python+Django+Vue构建智能招聘分析系统

1. 项目背景与核心价值 招聘信息泛滥的时代,求职者常常陷入"数据过载"的困境。我在帮学弟调试求职系统时发现,平均每个求职者需要浏览47个招聘页面才能找到匹配岗位,其中60%的时间浪费在重复筛选基础条件上。这个基于Python的招聘信…

2026/8/23 18:38:53 阅读更多 →
华为杯数学建模D题解析:从PISA数据到芯片资源排布的建模与优化

华为杯数学建模D题解析:从PISA数据到芯片资源排布的建模与优化

1. 赛题核心:从PISA数据到芯片资源排布的建模挑战每年华为杯中国研究生数学建模竞赛的D题,总是能精准地踩在技术前沿与产业痛点的交叉口上。2022年的D题,表面上看是分析PISA数据,但结合其核心关键词“芯片”与“资源排布”&#x…

2026/8/23 18:38:53 阅读更多 →
生产者消费者模式:设备软件里日志、报警、图像怎么不卡住业务线程

生产者消费者模式:设备软件里日志、报警、图像怎么不卡住业务线程

这篇解决一个问题:设备软件里业务线程在跑,同时要写日志、存数据库、存图像、上报 SECS。这些 IO 操作慢,直接在业务线程里做会卡住状态机。怎么写才能让业务线程「丢下就走」,后台慢慢处理。一、先从一个生活例子说起你去银行办业…

2026/8/23 18:38:53 阅读更多 →

最新新闻

数学建模竞赛实战:线性规划模型构建、求解与结果分析全流程指南

数学建模竞赛实战:线性规划模型构建、求解与结果分析全流程指南

1. 项目概述:线性规划在数学建模中的核心地位 线性规划,这个名字听起来可能有点学术,但在数学建模的实战领域里,它绝对算得上是“万金油”级别的存在。无论是国赛、美赛还是亚太杯,翻开历年的优秀论文,你几…

2026/8/23 20:38:47 阅读更多 →
贝叶斯控制赋能AI编程:构建能边干边学的代码智能体

贝叶斯控制赋能AI编程:构建能边干边学的代码智能体

1. 项目概述:当贝叶斯遇上代码智能体最近在折腾AI编程助手和自动化脚本时,我一直在思考一个问题:这些所谓的“智能体”在执行任务时,比如根据我的模糊指令“优化一下这个函数的性能”,它们是怎么做决策的?是…

2026/8/23 20:38:47 阅读更多 →
美赛D题SDG优先级建模:网络分析与多准则决策实战解析

美赛D题SDG优先级建模:网络分析与多准则决策实战解析

1. 项目概述:从赛题到实战的思维跃迁每年二月的美国大学生数学建模竞赛(MCM/ICM),对于众多理工科学生而言,都是一场思维与耐力的双重考验。2023年的D题,将目光投向了宏大的“联合国可持续发展目标”&#x…

2026/8/23 20:38:47 阅读更多 →
FileMover:开源文件自动化管理工具,告别手动整理与备份

FileMover:开源文件自动化管理工具,告别手动整理与备份

你的电脑硬盘是不是也经常被各种文件塞得满满当当?照片、文档、视频、压缩包混在一起,想整理却无从下手?手动筛选、复制、移动,不仅耗时费力,还容易出错,一个不小心就可能覆盖重要文件。今天要介绍的&#…

2026/8/23 20:37:47 阅读更多 →
从数学建模赛题看数据驱动决策:自行车功率优化实战解析

从数学建模赛题看数据驱动决策:自行车功率优化实战解析

1. 项目概述:从一道赛题看数据驱动决策的实战演练 2022年美国大学生数学建模竞赛(MCM/ICM)的A题,题目是“ Cycling Power Profile ”(自行车功率曲线)。当年这道题一出来,就在我们这些老建模人…

2026/8/23 20:37:47 阅读更多 →
基于ThinkPHP与Laravel的高校招聘系统开发实践

基于ThinkPHP与Laravel的高校招聘系统开发实践

1. 项目背景与核心需求高校招聘会求职系统是连接企业与应届毕业生的重要桥梁。传统线下招聘会存在时间短、信息不对称、效率低下等问题,而基于ThinkPHP和Laravel框架开发的移动端解决方案能有效解决这些痛点。这个系统需要实现的核心功能包括:企业端&…

2026/8/23 20:37:47 阅读更多 →

日新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/23 0:00:50 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/23 0:00:50 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/23 0:00:50 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/23 0:00:50 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/23 0:00:50 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/23 0:00:50 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/23 18:47:06 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/23 12:10:44 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/22 3:22:48 阅读更多 →