目录
“为什么我的代码总是if-else摞成山?” —— 来自被需求折腾秃头的程序员灵魂发问
一、从游戏角色说起——初识策略模式
最近在开发一个RPG游戏时遇到个难题:我们的英雄需要根据战场形势切换不同战斗风格——近战猛砍、远程射击、法术轰炸…难道要写成这样?
public void attack(String style) {
if ("melee".equals(style)) {
swingSword();
} else if ("range".equals(style)) {
shootArrow();
} else if ("magic".equals(style)) {
castFireball();
}
// 新需求来了又要加else if...
}
痛点警示:这样的代码不仅违反开闭原则,还让单元测试变成噩梦。直到我遇到了策略模式这个"救世主"!
二、策略模式的三板斧
2.1 传统流派——接口实现
// 策略接口(江湖规矩)
interface AttackStrategy {
void executeAttack();
}
// 具体策略(各显神通)
class SwordAttack implements AttackStrategy {
@Override
public void executeAttack() {
System.out.println("剑光如龙!造成80点物理伤害");
}
}
class ArrowAttack implements AttackStrategy {
@Override
public void executeAttack() {
System.out.println("百步穿杨!造成60点远程伤害");
}
}
// 环境类(战场指挥官)
class Hero {
private AttackStrategy strategy;
public void setStrategy(AttackStrategy strategy) {
this.strategy = strategy;
}
public void performAttack() {
strategy.executeAttack();
}
}
// 实战演示
Hero hero = new Hero();
hero.setStrategy(new SwordAttack());
hero.performAttack(); // 输出:剑光如龙!造成80点物理伤害
2.2 新潮玩法——枚举策略
public enum DiscountStrategy {
NEW_USER {
public double apply(double price) {
return price * 0.9;
}
},
VIP {
public double apply(double price) {
return price * 0.7;
}
},
PROMOTION {
public double apply(double price) {
return Math.max(price - 50, 0);
}
};
public abstract double apply(double price);
}
// 使用示例
double finalPrice = DiscountStrategy.VIP.apply(200); // 140.0
2.3 函数式编程——Lambda秒杀
public class PaymentProcessor {
private Consumer<Double> paymentStrategy;
public void setPaymentStrategy(Consumer<Double> strategy) {
this.paymentStrategy = strategy;
}
public void processPayment(double amount) {
paymentStrategy.accept(amount);
}
}
// 实战演示
PaymentProcessor processor = new PaymentProcessor();
// 支付宝策略
processor.setPaymentStrategy(amount ->
System.out.println("通过支付宝支付:" + amount + "元"));
// 信用卡策略
processor.setPaymentStrategy(amount -> {
System.out.println("刷卡金额:" + amount);
System.out.println("3D验证中...");
});
三、策略模式进化论
3.1 策略工厂模式(策略Plus版)
public class StrategyFactory {
private static Map<String, AttackStrategy> strategies = new HashMap<>();
static {
strategies.put("sword", new SwordAttack());
strategies.put("arrow", new ArrowAttack());
strategies.put("magic", new MagicAttack());
}
public static AttackStrategy getStrategy(String type) {
return strategies.getOrDefault(type, () ->
System.out.println("平A!造成10点伤害"));
}
}
// 使用示例
hero.setStrategy(StrategyFactory.getStrategy("arrow"));
3.2 Spring版策略模式(自动装配)
@Service
public class NotificationService {
@Autowired
private Map<String, NotificationStrategy> strategies;
public void send(String channel, String message) {
NotificationStrategy strategy = strategies.get(channel + "Strategy");
if (strategy != null) {
strategy.send(message);
}
}
}
// 定义策略接口
public interface NotificationStrategy {
void send(String message);
}
// 具体实现
@Service
public class EmailStrategy implements NotificationStrategy {
@Override
public void send(String message) {
// 发送邮件逻辑
}
}
四、什么时候该用策略模式?
✅ 完美场景:
- 一个系统需要动态切换算法
- 存在大量条件判断语句
- 需要隔离算法实现与使用
⚠️ 使用禁区:
- 策略数量爆炸(考虑用命令模式)
- 简单场景杀鸡用牛刀
- 策略需要访问私有成员时
五、实战案例:电商促销系统
// 策略接口
public interface PromotionStrategy {
BigDecimal applyPromotion(BigDecimal price, Integer quantity);
}
// 满减策略
class FullReduction implements PromotionStrategy {
public BigDecimal applyPromotion(BigDecimal price, Integer quantity) {
BigDecimal total = price.multiply(BigDecimal.valueOf(quantity));
return total.compareTo(BigDecimal.valueOf(200)) >= 0 ?
total.subtract(BigDecimal.valueOf(50)) : total;
}
}
// 折扣策略
class Discount implements PromotionStrategy {
public BigDecimal applyPromotion(BigDecimal price, Integer quantity) {
return price.multiply(BigDecimal.valueOf(quantity))
.multiply(BigDecimal.valueOf(0.8));
}
}
// 策略切换器
class PromotionSelector {
private static Map<String, PromotionStrategy> strategies = new ConcurrentHashMap<>();
static {
strategies.put("FULL_REDUCE", new FullReduction());
strategies.put("DISCOUNT", new Discount());
}
public static PromotionStrategy select(String promotionType) {
return strategies.getOrDefault(promptionType, (p, q) -> p.multiply(BigDecimal.valueOf(q)));
}
}
六、总结:策略模式的正确打开方式
🛠 设计哲学:
- 把变化的部分装进策略
- 面向接口编程而不是实现
- 用组合代替继承
💡 最佳实践:
- 优先使用函数式接口
- 大型项目使用策略工厂
- 结合Spring等框架自动管理
- 合理控制策略颗粒度
最后留个思考题:当策略需要携带状态时(比如折扣策略需要获取用户等级),该如何优雅地处理?欢迎在评论区分享你的高见!