策略模式到底该不该用?

522 阅读4分钟

关键词:设计模式 / 策略模式 / 面向对象 / 代码结构 / 工程思维


一、前言

作为一名计算机专业的学生,于大二本科初学设计模式课程之时,我曾对其充满向往和好奇。

“能轻松写出使用工厂、单例、观察者的代码并运用到实际业务开发中,是不是就离架构师不远了?”

然而,深入学习后我开始有一个疑问:

设计模式到底是“必须掌握的套路”,还是“有选择使用的工具”?

下面我将以策略模式为例,从动机、结构、优点再到误用的典型场景,逐步剖析它在开发中真正的价值,并分享我在学习过程中的一些反思和心得。


二、策略模式:定义与结构

策略模式的核心目的是封装算法的变化,将行为抽象出来,使得算法之间可以自由切换且互不影响。

模式结构

public interface Strategy {
    void execute();
}

public class StrategyA implements Strategy {
    public void execute() {
        System.out.println("执行策略A");
    }
}

public class StrategyB implements Strategy {
    public void execute() {
        System.out.println("执行策略B");
    }
}

public class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void run() {
        strategy.execute();
    }
}

调用方式:

Context context = new Context(new StrategyA());
context.run();

结构非常清晰:抽象策略接口、若干具体策略类、一个使用策略的上下文类。


三、模拟业务:会员折扣系统

假设我们正在实现一个“会员折扣”系统,不同等级的用户有不同的折扣规则:

  • 普通用户:无折扣
  • VIP 用户:9 折
  • 超级 VIP:8 折

一种“使用策略模式”的写法如下:

public interface DiscountStrategy {
    BigDecimal calculate(BigDecimal price);
}

public class NormalUserStrategy implements DiscountStrategy {
    public BigDecimal calculate(BigDecimal price) {
        return price;
    }
}

public class VipUserStrategy implements DiscountStrategy {
    public BigDecimal calculate(BigDecimal price) {
        return price.multiply(BigDecimal.valueOf(0.9));
    }
}

public class SuperVipUserStrategy implements DiscountStrategy {
    public BigDecimal calculate(BigDecimal price) {
        return price.multiply(BigDecimal.valueOf(0.8));
    }
}

通过配置 Map<String, DiscountStrategy>,我们就可以根据用户等级获取对应策略并计算价格。


四、看似合理,但真的是“最佳实践”吗?

通过模拟开发场景,我逐渐意识到:

策略模式的使用,并不总是带来正收益。

以下是策略模式在这个案例中可能引发的问题:

1. 策略逻辑过于简单

三种策略只有一行代码,抽象出三个类显得非常冗余。为了解耦,反而引入了复杂性。

2. 策略扩展性并不强

假设系统一年最多改一次折扣策略,引入扩展性强的设计模式显得“小题大做”。

3. 维护成本上升

多人协作开发时,过度抽象的结构反而降低了代码可读性,不如用清晰的 switch 更直观。


五、那应该怎么判断是否使用策略模式?

我总结出以下判断标准,供大家在面对策略型场景时思考是否引入设计模式:

判断维度使用策略模式不使用策略模式
策略种类≥ 3 类,差异明显≤ 2 类,逻辑简单
策略变动频率频繁新增或变更几乎固定
策略结构复杂度结构差异较大实现逻辑只有一两行
可维护性抽象更易管理代码越抽象越难维护

六、更轻量的实现方式?

当策略较少、逻辑简单时,使用 Java switchMap 映射就足够优雅了:

BigDecimal discount = switch (user.getLevel()) {
    case "NORMAL" -> price;
    case "VIP" -> price.multiply(BigDecimal.valueOf(0.9));
    case "SUPER_VIP" -> price.multiply(BigDecimal.valueOf(0.8));
    default -> price;
};

对比策略模式的类结构,这种写法在“适当的复杂度下”更容易理解与维护。


七、我对设计模式的学习反思

在阅读《Head First 设计模式》《大话设计模式》这类书籍时,我曾经希望每一个场景都能套一个模式。但渐渐明白:

  • 设计模式是为了解决“变化”带来的不稳定,而不是追求架构美感。
  • 不要为了设计模式而设计,而是为了“业务演化”准备好结构。
  • 没有银弹,也没有模式是永远的答案。

八、总结

策略模式是一种经典的行为型模式,适用于“算法族类问题”的场景。但在应用时应权衡其带来的抽象与复杂度。

合理使用策略模式的关键不在于掌握结构,而在于判断“是否真的需要它”。