新闻详情

新闻详情

首页 / 资讯中心 / 详情

策略模式:怎么让同一台设备干不同的活,还不用 if-else 硬塞

发布时间:2026/9/3 21:40:00来源:尧图网络
策略模式:怎么让同一台设备干不同的活,还不用 if-else 硬塞
这篇解决一个问题同一个模块要按不同规则干活怎么写才不用 if-else 堆成山也不用在运行时频繁换对象。从最直觉的写法开始一步步演进到策略模式最后落到设备软件的真实场景。一、先从一个生活例子说起你去餐厅吃饭餐厅有三种会员普通会员打 9 折银卡会员打 8 折金卡会员打 7 折。最直觉的写法double CalcPrice(double original, string memberType) { if (memberType 普通) return original * 0.9; if (memberType 银卡) return original * 0.8; if (memberType 金卡) return original * 0.7; return original; }能跑但问题藏在后面明天加「钻石会员打 6 折」要改这个函数。后天加「生日月额外 9 折」又要改。大后天要按地区不同打折继续改。这个函数越来越长每次改都可能碰到老逻辑。根本问题打折规则是「会变的策略」但你把它写死在了一个函数里。变规则就要改函数改函数就可能碰老代码。策略模式的思路是把每种打折规则单独抽成一个对象用的时候传进去想换规则换对象不改函数。二、一步步演进到策略模式第一步把规则抽成函数——稍微好一点double NormalDiscount(double price) { return price * 0.9; } double SilverDiscount(double price) { return price * 0.8; } double GoldDiscount(double price) { return price * 0.7; } double CalcPrice(double original, double (*discountFunc)(double)) { return discountFunc(original); } // 用 double price CalcPrice(100, GoldDiscount); // 70改进了CalcPrice不用知道有几种会员了传哪个函数用哪个。但问题是函数指针没有状态。如果金卡会员还要「累计消费满 1000 再减 50」纯函数搞不了需要带状态的类。第二步把规则抽成类——策略模式成型// 抽象策略所有打折策略都实现这个接口// 抽象策略所有打折策略都实现这个接口 class IDiscountStrategy { public: virtual double Calc(double originalPrice) 0; virtual ~IDiscountStrategy() default; }; // 具体策略普通会员 class NormalDiscount : public IDiscountStrategy { public: double Calc(double price) override { return price * 0.9; } }; // 具体策略银卡会员 class SilverDiscount : public IDiscountStrategy { public: double Calc(double price) override { return price * 0.8; } }; // 具体策略金卡会员带累计消费逻辑 class GoldDiscount : public IDiscountStrategy { double m_totalSpent 0; public: double Calc(double price) override { m_totalSpent price; double result price * 0.7; if (m_totalSpent 1000) result - 50; return result; } };使用方Contextclass Cashier { IDiscountStrategy* m_strategy; // 持有当前策略 public: void SetStrategy(IDiscountStrategy* s) { m_strategy s; } double Checkout(double originalPrice) { return m_strategy-Calc(originalPrice); // 委派给策略 } };Cashier cashier; cashier.SetStrategy(new GoldDiscount()); double price cashier.Checkout(100); // 70这就是完整的策略模式。三个角色角色例子Context上下文Cashier收银台持有策略委派计算Strategy抽象策略IDiscountStrategy定义接口ConcreteStrategy具体策略NormalDiscount/SilverDiscount/GoldDiscount关键Cashier不知道有几种会员、怎么打折。它只管「我有个策略结账时调它」。加新会员新建一个类SetStrategy传进去Cashier一行不改。三、策略模式解决了什么不用策略模式时double CalcPrice(double original, string type) { if (type 普通) ... else if (type 银卡) ... else if (type 金卡) ... else if (type 钻石) ... // 不断加 else if (type 生日) ... // 不断加 }问题清单一个函数越来越长几十个 if-else。加新规则要改老函数可能碰到老逻辑。规则之间互相干扰改一个分支不小心碰到另一个。没法带状态金卡的累计消费逻辑塞不进去。没法复用另一个项目要打折逻辑搬不走这个巨型函数。用策略模式后每种规则一个类互不干扰。加新规则新建一个类老代码不改。规则可以带状态可以复杂逻辑。规则类可以独立测试。规则类可以复用到别的项目。一句话策略模式把「变的部分」抽出去让「不变的部分」稳定。四、回到设备软件策略模式长什么样设备软件里到处是「同一模块按不同规则干活」的场景。举几个真实的。场景一搬运手按不同策略选站一台 Handler 有 Tray 手臂上料时从料道取料放到 Buffer下料时从 Buffer 取料放回料道。选哪个站取、选哪个站放、怎么匹配吸嘴和盘格规则完全不同。不用策略模式的写法void Arm::WorkFlow() { while (running) { if (m_mode Load) { // 上料选站逻辑先看放料站有没有空再回取料站取 // ... 50 行 } else if (m_mode Unload) { // 下料选站逻辑先看取料站有没有料再找放料站 // ... 50 行 } else if (m_mode Clear) { // 清料选站逻辑和下料类似但 Bin 类型不同 // ... 50 行 } // 明天加 Exchange 又是 50 行 } }WorkFlow越来越长每加一个模式改一次。用策略模式// 抽象策略 class TransportStrategy { public: virtual void TransportFlow() 0; virtual ~TransportStrategy() default; }; // 上料策略目标驱动先看放料站 class TargetTransport : public TransportStrategy { public: void TransportFlow() override { // 上料选站逻辑 } }; // 下料策略固定线路按起点终点推进 class FixedTransport : public TransportStrategy { public: void TransportFlow() override { // 下料选站逻辑 } }; // 手臂Context class Arm { TransportStrategy* m_transport; public: void WorkFlow() { m_transport-TransportFlow(); // 委派给策略 } };加新模式新建一个策略类。Arm一行不改。场景二分选手按不同 Bin 分料分选手从 Buffer 取料后要按 Bin 类型分到不同料仓Bin1 去满盘仓Bin2 去空盘仓NG 去废料盒。// 抽象策略 class ISortStrategy { public: virtual Station* SelectTarget(BinState bin) 0; }; // 按 Bin 分到不同仓 class BinSortStrategy : public ISortStrategy { public: Station* SelectTarget(BinState bin) override { if (bin BinState::Bin1) return m_pFullTank; if (bin BinState::Bin2) return m_pEmptyTank; if (bin BinState::NG) return m_pWasteBox; return nullptr; } }; // 分选手Context class SortingArm { ISortStrategy* m_sortStrategy; public: void WorkFlow() { Station* target m_sortStrategy-SelectTarget(currentBin); PlaceStation(target); } };换分 Bin 规则换策略对象SortingArm不改。场景三测试流程按产品不同走不同步骤同一台设备测不同产品测试步骤不同产品 A 先测电压再测电流产品 B 先测功能再测耐压。class ITestStrategy { public: virtual TestResult RunTest() 0; }; class ProductATest : public ITestStrategy { TestResult RunTest() override { TestVoltage(); TestCurrent(); return result; } }; class ProductBTest : public ITestStrategy { TestResult RunTest() override { TestFunction(); TestWithstand(); return result; } }; class Tester { ITestStrategy* m_testStrategy; public: void Run() { m_testStrategy-RunTest(); } };换产品换策略测试台代码不改。五、设备软件里策略模式的特殊用法配置驱动前面餐厅的例子是「运行时随时SetStrategy换」但设备软件里很多场景不是这样。设备软件里更常见的用法是初始化时挂一个策略运行时不换策略对象而是换策略内部的配置。以搬运手为例// 初始化时挂策略 arm-m_transport new TargetTransport(arm); // 上料时 arm-m_StLine.clear(); arm-m_StLine.push_back(StationLine(料道, Buffer, pos_Tray, {LoadIC})); // 下料时 arm-m_StLine.clear(); arm-m_StLine.push_back(StationLine(Buffer, 料道, pos_Tray, {Bin1}));策略对象TargetTransport从头到尾没换。上料下料的区别是m_StLine路线配置反过来了。为什么不换策略对象手臂类型和策略类型是产品形态绑定的Tray 手天生用 TargetBIB 手天生用 Fixed。同一套算法Target既能做正向料道→Buffer也能做反向Buffer→料道换配置就够。运行时delete策略再new一个要清理内部状态、处理互指指针风险高收益低。所以设备软件里策略模式的用法是教科书context.setStrategy(new B()) → 运行时频繁换设备软件init 时注入策略 → 运行时换配置 → 策略对象不动这不是「没用策略模式」而是策略模式的配置驱动变体。策略对象负责算法骨架配置负责具体任务单。六、什么时候该用策略模式判断标准很简单同一段逻辑里有多种「规则」且规则会变、会扩展。场景有没有多种规则会不会扩展用不用策略打折会员等级不同会加新等级用搬运选站上料/下料/清料会加新模式用分 BinBin 类型不同会加新 Bin用测试流程产品不同会加新产品用计算两数之和没有规则不会扩展不用读传感器只有读一种不会扩展不用反过来说如果逻辑只有一种规则、不会扩展硬抽策略就是过度设计。别为了用模式而用模式。七、策略模式的坑坑一策略类太多管理混乱十种规则就十个类二十种就二十个。如果没有统一注册机制new散落各处。对策用工厂 注册表集中创建策略对象不在业务代码里裸new。坑二策略和 Context 双向依赖策略要调 Context 的方法Context 又持有策略容易循环依赖。class TargetTransport : public TransportStrategy { Arm* m_arm; // 策略持有 Context }; class Arm { TransportStrategy* m_transport; // Context 持有策略 };对策策略通过抽象接口调 Context不直接依赖具体类。或者 Context 把策略需要的方法抽成接口策略只依赖接口。坑三策略内部状态没清理就复用策略对象带状态比如累计计数切换任务时忘了重置上次的状态影响这次。对策切换配置时调策略的Reset()或每次任务新建策略对象短生命周期策略。坑四把策略当万能锤不是所有 if-else 都要抽策略。如果分支只有两三个、不会扩展、逻辑简单if-else 比策略清晰。// 这个不用策略 if (mode Load) DoLoad(); else DoUnload();// 这个该用策略// 这个该用策略 if (mode Load) { ... 50行 ... } else if (mode Unload) { ... 50行 ... } else if (mode Clear) { ... 50行 ... } else if (mode Exchange) { ... 50行 ... } // 还会继续加判断标准分支超过三个、每个分支逻辑超过 20 行、会继续扩展才值得抽策略。八、可复用结论策略模式的本质把「变的部分」规则/算法抽成对象让「不变的部分」流程骨架稳定。三个角色Context持有策略、委派调用、Strategy抽象接口、ConcreteStrategy具体规则。设备软件的特殊用法初始化注入策略对象运行时换配置路线、参数改变行为不换策略对象。适合场景同一段逻辑有多种规则、规则会扩展、每个规则逻辑较复杂。不适合场景分支少、逻辑简单、不会扩展——if-else 更清晰。坑策略类用工厂管理、避免双向依赖、切换任务时重置状态、别过度设计。策略模式不是「消灭 if-else」而是「把会膨胀的 if-else 提前拆成独立类让每个规则自己管自己」。用对了加新规则只是加一个类用错了简单逻辑被拆得七零八落反而难读。关键是判断「这段逻辑会不会长、会不会变」——会就抽策略不会就老老实实 if-else。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

RH850F1L CodeFlash ECC校验机制与SD17固件交付规范解析 2026/9/4 1:47:00

RH850F1L CodeFlash ECC校验机制与SD17固件交付规范解析

简介:本资源是面向汽车电子功能安全开发的RH850/F1L芯片Code Flash ECC校验机制测试样例,专为使用该瑞萨32位车规级MCU进行ASIL B级功能安全(FuSa)软件开发的工程师及嵌入式学习者设计,解决Code Flash数据完整性验证与…

阅读更多 →
基于51单片机与霍尔传感器的自行车智能码表DIY全解析 2026/9/4 1:47:00

基于51单片机与霍尔传感器的自行车智能码表DIY全解析

简介:本资源是一套基于51单片机(STC12C5A60S2)实现的自行车数字式多功能码表完整工程,面向自动化、通信、电子类专业学生及嵌入式初学者,解决车速实时测量、里程累计、超速报警与OLED可视化显示等典型嵌入式应用问题&a…

阅读更多 →
基于51单片机与PT100的温度报警系统:从传感器原理到Proteus仿真全解析 2026/9/4 1:47:00

基于51单片机与PT100的温度报警系统:从传感器原理到Proteus仿真全解析

简介:本资源是一套面向高校电子类专业本科生的51单片机毕业设计实践方案,聚焦火灾预警场景下的温度实时监测与报警功能实现,适用于课程设计、毕设选题及嵌入式入门学习。项目以AT89S52/STC89C52等经典51单片机为核心,结合PT100热电…

阅读更多 →
芭比摇滚训练营玩具:音乐启蒙与儿童创造力培养指南 2026/9/4 1:47:00

芭比摇滚训练营玩具:音乐启蒙与儿童创造力培养指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Unity动作游戏技能系统架构:从状态机到数据驱动的工程实践 2026/9/4 1:47:00

Unity动作游戏技能系统架构:从状态机到数据驱动的工程实践

动作游戏技能系统,往往是项目从原型走向可玩版本的第一个分水岭。前期直接写单机逻辑确实爽快:按下攻击键、播放动画、碰撞体生效、数值掉血,一套连招打下来一气呵成。但一旦技能数量从两三个膨胀到几十个,加 Buff、打断、连招、霸…

阅读更多 →
STC8G1K17驱动CS1238与ST7565:嵌入式数据采集显示系统全解析 2026/9/4 1:44:00

STC8G1K17驱动CS1238与ST7565:嵌入式数据采集显示系统全解析

简介:本资源是一套面向嵌入式初学者与单片机开发者的完整驱动实践工程,聚焦STC8G1K17(高性能8051内核)对CS1238模拟开关的精准读写控制,以及通过ST7565驱动芯片操控LCD12864点阵液晶实现人机信息显示。适用于工业测控、…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞