玩一款射击游戏,面对不同敌人时需要切换武器:霰弹枪近战无敌,狙击枪远程致命,冲锋枪适合突袭。如果每次换枪都要重开游戏,显然不合理。策略模式就像游戏中的武器切换系统,让你在运行时动态更换算法,无需修改核心代码。
一、策略模式解决的问题
假设你正在开发一个物流配送系统,需要根据货物类型选择运输方式:
生鲜食品:冷链专车(成本高但速度快)
普通货物:标准货车(性价比高)
超大件:平板卡车(需特殊装卸)
如果用if-else硬编码,代码会变成:
if 货物类型 == 生鲜:
选择冷链专车
elif 货物类型 == 普通:
选择标准货车
elif 货物类型 == 超大件:
选择平板卡车
当新增“危险品”运输策略时,必须修改核心代码,这违反了开闭原则。策略模式正是为解决这种“算法耦合”问题而生。
二、策略模式的三大角色
(1) 抽象策略(Strategy)
定义所有算法的公共接口(如transport()方法)
类似“武器接口”,不关心具体是枪还是刀
(2) 具体策略(ConcreteStrategy)
实现具体算法(如冷链专车、标准货车的transport()方法)
对应霰弹枪、狙击枪等具体武器逻辑
(3)上下文(Context)
持有策略对象的引用(如“士兵”持有当前武器)
客户端通过它调用策略,无需知道具体实现细节
三、实例:物流运输策略系统
物流公司需要支持多种运输方式,要求能动态切换策略且不影响原有代码。
(一)UML图:
(二)伪代码实现:
(1) 抽象策略接口
interface TransportStrategy {
function transport(goods);
}
(2) 具体策略1:冷链运输
class ColdChainStrategy implements TransportStrategy {
function transport(goods) {
print("冷链专车运输:" + goods + ",温度保持-18℃")
}
}
(3) 具体策略2:标准运输
class StandardTruckStrategy implements TransportStrategy {
function transport(goods) {
print("标准货车运输:" + goods)
}
}
(4) 上下文类
class LogisticsContext {
private strategy: TransportStrategy
function setStrategy(newStrategy) {
this.strategy = newStrategy
}
function deliverGoods(goods) {
this.strategy.transport(goods)
}
}
(5)客户端使用
logistics = new LogisticsContext()
logistics.setStrategy(new ColdChainStrategy())
logistics.deliverGoods("进口牛肉") // 输出:冷链专车运输进口牛肉
logistics.setStrategy(new StandardTruckStrategy())
logistics.deliverGoods("服装箱包") // 输出:标准货车运输服装箱包
四、优缺点分析
(一)优点:
算法自由切换:像换武器一样替换策略(运行时动态修改);
消除条件语句:用组合替代if-else,代码更清晰;
扩展友好:新增策略只需实现接口,无需修改现有代码;
策略隔离:各策略相互独立,可单独维护。
(二)缺点:
类数量增加:每个策略都需要单独类;
客户端需知策略:需要了解有哪些可用策略(可通过工厂模式优化);
策略公开:所有策略对客户端可见,可能暴露实现细节。
五、典型应用场景
算法族需要动态切换:如文件压缩(ZIP/RAR/7z选择);
需要隐藏算法实现:如支付系统(支付宝/微信支付/银行卡);
多条件分支优化:替代复杂的if-else或switch-case;
需要隔离不同策略:如游戏AI的不同难度模式;
需要扩展策略:如电商促销(满减/折扣/优惠券组合)。
策略模式就像“瑞士军刀”,无论需要开瓶器、剪刀还是小刀,只需切换对应工具即可,而无需携带整套单独的刀具。这种“即插即用”的设计思维,正是软件可维护性的关键。