资讯动态

策略模式实战指南:从Java/C++到游戏开发与重构

发布时间:2026/10/2 15:06:33 来源:尧图企业网站定制
写策略模式的文章很多教程都把它讲得太简单了定义一个接口写几个实现类然后再来一个Context完事。这种理解不能说错但离“会用”还差得很远。我做了这么多年的后端和游戏开发策略模式是我见过被误用最多的行为型模式也是用好之后收益最明显的模式之一。这篇文章我从头讲一遍策略模式但重点放在怎么把它落地到真实项目里——从需求分析、代码重构到不同语言下的实现差异再到真实世界里最容易踩的坑一次说清楚。设计模式这东西是个很特殊的技能要用它先得从里到外理解它。策略模式的核心不复杂定义算法族分别封装起来让它们可以互相替换。就这么一句话的事儿但展开以后牵扯到开闭原则、组合优于继承、甚至工厂模式和它在进化中的分合关系。尤其是“让它们可以互相替换”这半句只可意会不可言传的情况特别多。这篇文章适合谁看呢如果你正在学设计模式准备面试如果你想把手头的if-else重构一下如果你正在写游戏里的AI或者战斗技能再或者你只是想知道策略模式到底“好”在哪这篇文章都能提供一些真实可用的东西。1. 策略模式全貌与设计思路拆解1.1 从一段“烂代码”说起大家写业务代码最常见的场景就是根据不同的条件走不同的逻辑。举个例子电商系统里的运费计算普通用户按标准价格VIP用户打八折超级VIP打五折新人免邮搞活动的时候全场一口价。写成代码就是一大串if-elsepublic BigDecimal calculateFreight(User user, Order order) { if (user.getLevel() 1) { return order.getWeight().multiply(new BigDecimal(10)); } else if (user.getLevel() 2) { return order.getWeight().multiply(new BigDecimal(10)).multiply(new BigDecimal(0.8)); } else if (user.getLevel() 3) { return order.getWeight().multiply(new BigDecimal(10)).multiply(new BigDecimal(0.5)); } else if (order.isNewUser()) { return BigDecimal.ZERO; } else { return order.getWeight().multiply(new BigDecimal(12)); } }这个代码的毛病用一句话说就是当“玩法”变多的时候这个方法就失控了。今天加一个“校园认证用户七折”明天加一个“地区补贴运费”后天产品经理说“大促期间全场满99包邮”你会发现这个方法的判断条件越来越多每个条件里还可能继续嵌套代码的可读性、可测试性都会崩掉。更深层次的问题在于这种写法违背了开闭原则每增加一种计费规则你都要去修改这个已经工作得很好的方法而不是新增一段独立的代码。修改老代码就意味着回归测试意味着可能引入不该有的bug意味着两个业务方的逻辑被强行塞进了一个函数里迟早爆发冲突。策略模式就是为了解决这样的问题而存在的。它的思路也很符合直觉把每一种算法都装进一个独立的、可替换的类里让调用方在运行时选择想要的行为。1.2 策略模式的本质与定位策略模式属于GoF 23种设计模式中的行为型模式它的定义非常简洁定义一组算法将每个算法都封装起来并使它们之间可以互换。我拆开讲**“定义一组算法”说的是你要先抽象出几个算法之间共同的操作接口比如前面运费计算里的“都是输入用户和订单输出运费”“将每个算法都封装起来”说的是每个算法要独立成类自己内部的事情自己解决跟其他算法互不干扰“使它们之间可以互换”**说的是对调用方来说这几个算法的类应该长得一模一样换掉一个实现调用方代码一行都不用改。这个模式的核心价值用一句特别实在的话概括把“做什么”和“怎么做”分开。利用多态让“怎么做的细节”在运行时由客户端动态决定。为什么说策略模式是“多用组合、少用继承”的代表因为继承实现代码复用有个天然的缺陷你继承的是整个类包括它所有的公开行为很多你可能并不需要。策略模式则是组合的思路——你的主类通常叫Context即上下文持有一个策略接口的引用这个引用指向什么具体实现完全由你来控制。你不需要再为了“某个用户打八折”去创建一个“VIP运费计算器”的子类而是把变化的部分委托给一个独立的对象。1.3 策略模式的三个角色与职责划分策略模式只涉及三个角色很多人画类图画得非常热闹其实根本角色就三个策略接口Strategy定义了一个算法的公共接口算运费时它就是calculateFreight(User, Order)这个方法。Java下可以是接口也可以用抽象类关键在于让所有策略实现类对外提供一致的行为。具体策略类ConcreteStrategy实现了策略接口封装了具体的算法比如VIPFreightStrategy、NewUserFreightStrategy、PromotionFreightStrategy。每个类各管各的规则互不依赖。上下文Context这是我发现最容易被新手忽略的角色。上下文持有策略接口的引用负责把具体的算法调用委托给策略对象。它本身不实现算法只是“掌握”算法。比如运费计算服务FreightService它接收一个策略对象对外暴露calculateFreight方法内部调用strategy.calculateFreight(user, order)。这三者的关系可以用一个生活化的类比策略模式像吃饭点菜。菜单是策略接口每一道菜是具体策略而服务员是上下文——你不关心厨房怎么做宫保鸡丁你只告诉服务员你要什么菜做好的端上来就是策略接口统一返回的结果。有一个关键认知必须反复强调策略模式本身不负责选择使用哪个策略。选择的事要么是客户端自己决定好把哪个策略传进来要么通过工场模式、注册表等机制来辅助完成。很多人画策略模式类图时会在上下文里画一个switch-case来选策略这其实是把选择策略的逻辑错误地塞进了上下文等会儿在“常见问题”部分我会专门说这个。2. 策略模式核心细节与落地要点2.1 策略模式的结构图谱代码级拆解虽然不能用图表但策略模式的结构用文字描述完全可以讲清楚。整个体系就三条线Strategy接口定义算法的方法签名。ConcreteStrategy类实现该接口一个类对应一种算法。Context类组合而不是继承Strategy接口并提供给外界调用的方法。用Java代码表示这个结构最标准的样子是这样的// 策略接口 public interface FreightStrategy { BigDecimal calculate(User user, Order order); } // 具体策略普通用户 public class NormalUserFreightStrategy implements FreightStrategy { Override public BigDecimal calculate(User user, Order order) { return order.getWeight().multiply(new BigDecimal(10)); } } // 具体策略VIP用户 public class VipUserFreightStrategy implements FreightStrategy { Override public BigDecimal calculate(User user, Order order) { return order.getWeight().multiply(new BigDecimal(10)).multiply(new BigDecimal(0.8)); } } // 上下文角色 public class FreightContext { private FreightStrategy strategy; public FreightContext(FreightStrategy strategy) { this.strategy strategy; } public void setStrategy(FreightStrategy strategy) { this.strategy strategy; } public BigDecimal calculateFreight(User user, Order order) { return strategy.calculate(user, order); } }注意这里的setStrategy方法它的存在意义很大——策略模式之所以能“运行时动态切换策略”靠的就是这个setter。你可以在处理订单的过程中根据实时数据把策略从前一个切换成后一个比如用户中途升级成VIP运费计算策略立刻换成VIP的那套调用方无需感知变化。2.2 策略接口参数的两种传递方式很多新手写策略模式的时候卡在一个问题上策略方法需要的参数从哪里来常见的有两种做法从Context构造时传入Context在创建时就把数据都准备好之后在调用策略方法时不需要额外参数。这种方式的优点是调用简单缺点是Context需要知道所有的数据细节而且数据变化频繁时你得频繁new Context。从策略方法中传入就是上面代码的样子calculate(User user, Order order)把需要的参数当作方法入参。这种方式更透明也更好测试——你可以直接对某个策略类单独调用方法测试而不必构造Context。我强烈推荐这种方式原因很简单策略模式的生命周期里策略参数往往不是固定的让策略方法自己去拿参数会灵活很多。我个人在真实项目中总结的经验是策略接口的方法入参应该尽量抽象不要出现“订单DTO”“用户DTO”这种具体业务对象满天飞的场景。比较理想的状态是策略接口依赖的是抽象的参数对象比如“运费计算参数”这个聚合对象把用户、订单、商品清单等包装在一起。这样后续增加新的策略时参数不会成为扩展的障碍。2.3 无状态策略与单例优化策略类有两大类有状态和无状态。无状态策略只包含算法逻辑不包含任何成员变量每次执行都依靠方法入参进行计算。这种策略天然是线程安全的也天然适合复用——完全可以做成单例全局共享。有状态策略则往往需要内部维护一个状态变量比如根据迭代轮数调整算法的“动态折扣策略”。有状态的策略要注意线程安全问题而且作为Context持有的对象要小心它的生命周期。我自己的习惯是默认把所有策略类都设计成无状态的从单例池里去获取实例。这样做不但节省了频繁创建对象的开销还有个隐藏的好处——你可以在上下文里放心地缓存策略对象不用担心状态污染。2.4 策略设计的三条实用原则写策略模式时接口设计的好坏直接影响整个模式的使用舒适度。三条原则是我从无数次重写中提炼出来的原则一策略接口的粒度要适中。接口方法如果太细策略内部要实现的方法太多策略类爆炸如果太粗所有策略都只有一两个方法复用性又差。判断的标准是“算法族”的抽象运费计算算法族的共同点是“输入用户订单输出费用”这个粒度就够了不需要把计算明细也暴露出来。原则二策略接口的语义要一致。所有策略实现的行为语义应该一致不应该出现某个策略返回运费、另一个策略返回null代表“无法计算”这种不一致的约定。不一致的语义会让调用方不得不去检查返回值类型策略模式的“可互换”就付之东流了。原则三策略要围绕“变化点”来设计。策略模式的目的是封装变化。你的代码里哪些地方最可能变用户等级的折扣活动的规则这些都是变化点。围绕变化点去设计策略接口比为了设计而设计要实用得多。如果整个业务逻辑只有一两个if分支未来也基本稳定硬套策略模式反而是画蛇添足。3. 实操Java实现与一次完整重构3.1 场景定义与初始代码用一个完整的业务场景来演示策略模式的落地还是用运费计算但这个场景复杂一点不同用户类型、不同商品类型、不同地区、不同活动状态下运费计算规则完全不同。这几乎是电商系统里最典型的多策略场景。一开始的代码就是最糟糕的“多分支大杂烩”我见过真实项目里有人写出两百多行的运费计算方法各种if-else嵌套中间还夹着循环和线程池调用看代码的人精神都快崩溃了。这里用一个简化版来演示public BigDecimal calculateFreight(User user, Goods goods, Address address) { if (user.isVip()) { if (goods.getType() GoodsType.FRESH) { return baseFreight(address).multiply(new BigDecimal(0.9)); } return baseFreight(address).multiply(new BigDecimal(0.8)); } if (user.isNewUser()) { if (address.getProvince().equals(偏远地区)) { return BigDecimal.ZERO; } return baseFreight(address).multiply(new BigDecimal(0.5)); } if (goods.getType() GoodsType.FRESH) { return baseFreight(address).multiply(new BigDecimal(1.2)); } return baseFreight(address); }这段代码的问题稍微有经验的开发一眼就能看出第一可读性差没有人能一眼明白“偏远地区”为什么能打折第二扩展性差每增加一个用户类型或者商品类型就要往这个方法里塞新的分支第三可测试性差测试这个方法必须构造一整套用户、商品、地址的组合非常笨重。3.2 重构步骤三步走向策略化重构这个运费计算我建议分三步走每一步都让代码变得更好一点第一步抽象出策略常量与方法。先不要急着建类把你所有的分支逻辑都列出来看看里面一共有多少种“算法变体”。运费计算的变体无非是VIP价、新人价、生鲜溢价、偏远地区免费再加上一个默认价归纳出来大概五种。这一步的核心是“盘点变化”。第二步把每种变体独立成策略类。每种变体一个类继承统一策略接口。如果某些变体之间有公共逻辑比如都要算基础运费可以抽一个抽象类作为公共父类让几个具体策略继承。第三步让上下文统一调用策略。原方法的名字、入参、出参尽量保持不变防止外部代码大量修改但内部改为接收策略类。这就是业界常说的“保持API稳定逐步替换实现”。这个重构过程最有价值的地方在于你不需要一口气把所有分支全部移除完全可以一个分支一个分支地迁移每迁移一个就做一次回归测试。这种渐进式重构能够大幅降低重构风险。3.3 重构后的完整代码把运费计算真正实现成策略模式的话核心代码是这样的// 策略接口 public interface FreightStrategy { BigDecimal calculate(User user, Goods goods, Address address); } // 默认价 public class NormalFreightStrategy implements FreightStrategy { Override public BigDecimal calculate(User user, Goods goods, Address address) { return FreightCalculator.baseFreight(address); } } // VIP价 public class VipFreightStrategy implements FreightStrategy { Override public BigDecimal calculate(User user, Goods goods, Address address) { BigDecimal base FreightCalculator.baseFreight(address); if (goods.getType() GoodsType.FRESH) { return base.multiply(new BigDecimal(0.9)); } return base.multiply(new BigDecimal(0.8)); } } // 新人价 public class NewUserFreightStrategy implements FreightStrategy { Override public BigDecimal calculate(User user, Goods goods, Address address) { if (address.getProvince().equals(偏远地区)) { return BigDecimal.ZERO; } return FreightCalculator.baseFreight(address).multiply(new BigDecimal(0.5)); } } // 上下文运费计算服务 public class FreightContext { private FreightStrategy strategy; public FreightContext(FreightStrategy strategy) { this.strategy strategy; } public BigDecimal calculateFreight(User user, Goods goods, Address address) { return strategy.calculate(user, goods, address); } }使用的时候客户端根据用户类型自己选择策略FreightStrategy strategy; if (user.isVip()) { strategy new VipFreightStrategy(); } else if (user.isNewUser()) { strategy new NewUserFreightStrategy(); } else { strategy new NormalFreightStrategy(); } BigDecimal freight new FreightContext(strategy).calculateFreight(user, goods, address);你可能会问这也没省掉多少if-else啊原来的if-else还在只是从运费计算方法里搬到了客户端。这个问题问得很到位。策略模式解决的并不是“消灭所有if-else”它解决的是“把分散在算法内部的、容易搞混的分支逻辑收敛成一个个独立类把‘怎么算’的复杂度从调用方剥离”。至于“选哪个策略”的if-else本身是一种选择逻辑可以进一步用策略工厂来消除。策略模式和工厂模式搭档干活是实际项目里最常见的配合方式。3.4 用工厂模式驯服策略选择策略选择逻辑如果也散落在各个业务代码里同样会重复出现。解决方案是加一个策略工厂把“如何创建策略”的逻辑归一public class FreightStrategyFactory { private static final MapString, FreightStrategy STRATEGY_MAP new HashMap(); static { STRATEGY_MAP.put(vip, new VipFreightStrategy()); STRATEGY_MAP.put(new_user, new NewUserFreightStrategy()); STRATEGY_MAP.put(normal, new NormalFreightStrategy()); } public static FreightStrategy getStrategy(String userType) { return STRATEGY_MAP.getOrDefault(userType, new NormalFreightStrategy()); } }策略工厂加进去以后业务侧的代码就变得非常清爽获取策略、执行策略异常情况还有默认策略兜底。如果将来新增一个“团队用户”类型只需要新建一个策略类在工厂里注册一下业务侧一行不用动。这就是策略模式配合开放封闭原则带来的实际好处。补充一个经验如果策略的种类特别多比如几十种再往工厂的静态代码块里堆注册代码就有点臃肿了。这时候可以考虑用Spring把每个策略注册成一个Bean然后用策略类型名称来装配或者写一个注解驱动注册器通过反射自动把策略加载进Map。反射的方式初始化开销大一点但注册的过程几乎不用维护代码各有利弊。4. C实现差异与游戏开发场景4.1 C下的策略模式实现C实现策略模式和Java大同小异但C的生态有自己的习惯和约束特别是内存管理和模板机制。最经典的写法是利用虚函数实现多态class FreightStrategy { public: virtual ~FreightStrategy() default; virtual double calculate(const User user, const Goods goods, const Address address) 0; }; class NormalFreightStrategy : public FreightStrategy { public: double calculate(const User user, const Goods goods, const Address address) override { return baseFreight(address); } }; class VipFreightStrategy : public FreightStrategy { public: double calculate(const User user, const Goods goods, const Address address) override { double base baseFreight(address); if (goods.getType() GoodsType::FRESH) return base * 0.9; return base * 0.8; } }; class FreightContext { private: std::unique_ptrFreightStrategy strategy; public: explicit FreightContext(std::unique_ptrFreightStrategy s) : strategy(std::move(s)) {} void setStrategy(std::unique_ptrFreightStrategy s) { strategy std::move(s); } double calculateFreight(const User user, const Goods goods, const Address address) { return strategy-calculate(user, goods, address); } };C里用unique_ptr或shared_ptr管理策略对象的生命周期可以避免裸指针带来的内存泄漏。注意FreightContext持有了策略的唯一所有权如果你希望多个Context共享同一个策略那就要用shared_ptr。C还有一个Java没那么多用的进阶玩法用模板替代虚函数。当策略在编译期就能确定的时候利用模板实现策略模式可以省掉虚函数调用的开销这在游戏引擎这种性能敏感的场景里很有价值templatetypename Strategy class FreightContext { private: Strategy strategy; public: double calculateFreight(const User user, const Goods goods, const Address address) { return strategy.calculate(user, goods, address); } }; FreightContextNormalFreightStrategy context;这种方式做出来的Context类型是编译期绑定的性能几乎为零损耗代价是运行时无法切换策略。具体选哪种就看你的使用场景了运行时配置驱动的业务系统用虚函数战斗数值算法这类编译期定死的场景可以用模板。另外现代C里还有一种“轻量策略”的玩法直接用std::function作为策略类型不需要建策略类。这尤其适合策略逻辑非常简单、只有几行代码的场景class FreightContext { private: std::functiondouble(const User, const Goods, const Address) strategy; public: void setStrategy(std::functiondouble(const User, const Goods, const Address) s) { strategy std::move(s); } };std::function可以保存lambda、函数指针、仿函数非常灵活。但它放弃了面向对象的“策略类”抽象适用于策略集合不要求扩展性的情况。如果政策会不断变我还是建议用类的方式扩展和维护更清晰。4.2 游戏开发里的策略模式AI与技能游戏开发是策略模式的重度使用区。很多刚入行的游戏开发设计NPC的AI时习惯这样写判断玩家距离近了就攻击中等距离就放技能远了就巡逻再远了就追击。一段update里的if-else链从几十行写到几百行。用策略模式重构思路很清晰抽象“行为策略”接口class IBehaviorStrategy { public: virtual ~IBehaviorStrategy() default; virtual void execute(Monster* monster, float deltaTime) 0; };每个行为一个策略类巡逻策略、追击策略、攻击策略、逃跑策略。怪物自身作为上下文持有当前激活的行为策略。根据状态机或者行为树触发切换时调一下setStrategy就行。这里有一个特别重要的启示策略模式里策略的切换本身就相当于一个简单的状态机这也是很多人搞混策略模式和状态模式的原因。其实这两者区别很明显状态模式强调的是“状态之间的迁移规则”每个状态知道自己从哪里来、能去到哪里策略模式强调的是“算法可替换”策略之间是孤立的互不感知。在游戏AI里你用状态模式做状态迁移是合理的但具体每个状态内部怎么做那就是策略模式的天下了。两者是分层配合的关系。另一个游戏开发常用的场景是技能系统不同的技能有不同的伤害计算、冷却管理、特效触发逻辑。把每个技能都做成一个策略类玩家释放技能时技能管理上下文根据技能ID切换到对应的技能策略。这样做的好处很实际新技能上线不需要动游戏主循环的逻辑策划配置一个策略类加进去就行。4.3 策略模式与桥接模式、命令模式的边界策略模式很容易和另外两个模式搞混这里花点时间说清楚桥接模式它的核心是“抽象与实现分离”让两个维度可以独立变化。比如一个消息管理系统消息类型短信、邮件、微信和发送渠道国内、海外、加密是两个维度用桥接模式让它们自由组合。策略模式和桥接模式的类图很像但关注点不同策略模式关注“在同一个维度上的不同算法替换”桥接模式更关注“两个维度解耦可以自由搭配”。命令模式命令模式的目的是“请求发起者”和“请求执行者”解耦把请求本身封装成对象支持撤销、队列、日志等功能。策略模式关注的是算法替换两者最直接的区别是命令对象持有接收者和具体动作执行的是同一个动作的不同上下文策略对象持有的是算法切换的是整个算法的实现。简单来说你可以用策略模式实现命令模式里的“命令执行策略”但它们的意图角色完全不同不要看到几个类长得像就划等号。设计模式最重要的不是类图长什么样而是它解决的是什么问题。5. 常见问题与排查技巧实录5.1 策略类爆炸问题策略模式最容易被吐槽的一个点策略数量一多类的数量成倍增长。十个促销规则就是十个类文件多到看不完。这个问题我遇到过很多次有几个行之有效的方法方法一合并细粒度策略。有些策略极其简单只差一两个参数比如“打八折”和“打九折”完全可以用同一个策略类把折扣率作为构造参数传入没必要每个折扣率做一个类。方法二用内部类或Lambda。Java 8之后简单的策略直接可以用Lambda表达式创建不需要独立文件FreightStrategy strategy (user, goods, address) - baseFreight(address).multiply(new BigDecimal(0.8));这样一来策略的选择逻辑和策略实现可以写在同一个文件里在策略逻辑非常短的时候相当方便。方法三用配置驱动策略。把策略的“参数”设计成可配置的比如折扣率、免费门槛这些数字放在配置中心策略类做成通用的“配置参数策略”这样十几条相近规则就可以共用一个通用策略类只是配置不同。这种做法的关键在于策略接口不要绑死具体的参数对象而是传入一个配置模型。策略类爆炸往往说明你的策略粒度划分有问题。好的策略划分应该遵循“算法族”的概念每个策略对应一个真正独立的算法而不是对应一个特定的参数取值。5.2 空指针与默认策略策略工厂在Map里找不到对应策略时如果返回null调用方拿到null调方法直接空指针。我见过不止一次线上故障就是这么出来的。解决这个问题我推荐一个很经典的东西——空对象模式Null Object Pattern它是策略模式最常见的搭档之一。public class NoSuchFreightStrategy implements FreightStrategy { Override public BigDecimal calculate(User user, Goods goods, Address address) { // 降级走默认价同时打日志告警 return FreightCalculator.baseFreight(address); } }让策略工厂在找不到策略时返回这个默认策略而不是返回null。这样调用方永远不需要判断null代码更安全也更符合“对象不会为空的约定”。降级逻辑和告警逻辑也集中在一个地方管理非常顺手。5.3 策略切换时的并发与状态污染如果你把策略类做成了有状态类而且这个策略类还被全局共享了那并发环境下极容易出现状态污染。我遇见一个典型case一个“区域限购策略”内部保存了一个最近一次计算的商品ID线上同时有多个请求时有的请求拿到的是别的商品的限购结果数据错得莫名其妙。排查这个问题时一开始完全没想到是策略的状态污染——因为代码看起来没有任何共享变量的改动。后来通过在策略类里加了最大成员变量的排查才定位到问题是状态字段被并发写入。修复方式很简单把策略类改成无状态的所有数据都从方法入参拿。这个教训值得强调策略类坚决不做有状态设计。如果策略确实需要一些内部缓存比如计算历史请给Context单独维护不要在策略层存储。上下文是每个请求一个的而策略类是可能被所有请求共享的这是设计上必须明确的分工。5.4 常见问题速查表问题现象排查思路解决方案策略类数量爆炸新增规则时文件数量暴增检查策略粒度是否过细合并参数,使用配置化或Lambda策略工厂返回null调用处空指针看选择逻辑是否有默认兜底空对象模式兜底,返回默认策略策略并发状态污染结果间歇性错误查找策略类成员变量改为无状态,数据从入参传入上下文耦合具体策略切换策略时Context代码修改检查Context是否引用了具体策略类只依赖策略接口,用工厂创建策略策略方法违背LSP某个策略行为异常检查各策略是否做到语义一致统一策略入参出参语义5.5 策略模式不是万能药写策略模式写多了人会陷入一种迷思觉得所有分支都能套策略模式。我劝你冷静策略模式适合的场景是“算法族明确、变化点清晰”的地方。如果你面临的问题是“这个过程有三十个步骤步骤之间还相互依赖”那就不是策略模式该管的事该考虑模板方法模式或者责任链模式。如果只是两个小的if分支那也别费那个劲直接写判断代码更直观。判断是否使用策略模式的唯一标准这里有真正的“算法族”吗有那就用没有别强求。结尾一点个人的经验之谈我刚开始学策略模式的时候觉得它实在太“简单”了就是多态包装了一下看不出什么高深的技术含量。直到后来在真实项目里把一个两百行的运费计算方法重构完突然意识到设计模式的威力不在于它有多复杂而在于它把一个容易失控的变化点用最朴素的方式隔离住了。从那以后我写代码习惯性地问自己——这段逻辑未来会不会变会变的话变的是哪些部分这个“识别变化点”的能力比背下二十三个模式的类图重要得多。策略模式是我用的最多的模式之一也是最容易用错的一个希望这篇掏心窝子的分享能帮你少走一些弯路。真正理解一个模式靠的是在项目里一遍遍踩坑、一遍遍重构光看文章是不够的动手写一写比什么都有用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑