21.让代码活起来的策略模式:解锁Java中的灵活设计

“为什么我的代码总是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)));
    }
}

六、总结:策略模式的正确打开方式

🛠 设计哲学

  • 把变化的部分装进策略
  • 面向接口编程而不是实现
  • 用组合代替继承

💡 最佳实践

  1. 优先使用函数式接口
  2. 大型项目使用策略工厂
  3. 结合Spring等框架自动管理
  4. 合理控制策略颗粒度
Context
-strategy: Strategy
+setStrategy(Strategy)
+executeStrategy()
«interface»
Strategy
+execute()
ConcreteStrategyA
+execute()
ConcreteStrategyB
+execute()

最后留个思考题:当策略需要携带状态时(比如折扣策略需要获取用户等级),该如何优雅地处理?欢迎在评论区分享你的高见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhysunny

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值