策略模式和工厂模式的区别?

#中间件#面试八股

策略模式和工厂模式的区别?

结论先行:工厂管"造",策略管"做"。工厂模式解决"对象怎么创建、由谁创建",创建完就退场;策略模式解决"同一件事有多个算法,运行时怎么替换",对象要在整个调用期被使用。两者可以叠加——用工厂造策略,用策略消分支,这是工程里最常见的组合。

1. 逐项对比

对比项工厂模式策略模式
一句话意图把创建逻辑集中,调用方不 new把算法族封装,运行时可替换
解决的问题创建的耦合行为的耦合
客户端关心什么只要产品,不关心实现类知道有哪些策略,或由上下文选择
返回/产出一个对象一次行为结果
生命周期创建后交给调用方上下文持有,持续参与调用
变化维度产品种类算法种类
典型例子BeanFactory、PayChannelFactory计价规则、路由策略、压缩算法

2. 结构差异:一条是"生产链",一条是"调用链"

工厂模式:造完就退场
┌────────┐  create()  ┌──────────┐  ┌──────────┐
│ Client │───────────►│ Factory  │─►│ Product  │
└────────┘            └──────────┘  └──────────┘

策略模式:每次调用都经过上下文
┌────────┐  execute()  ┌──────────┐  ┌──────────┐
│ Client │────────────►│ Context  │─►│ Strategy │◄── 运行时可替换
└────────┘             └──────────┘  └──────────┘

看图的差别:工厂的箭头到产品就结束了;策略的箭头在"上下文"和"算法"之间来回。

3. 只有工厂:解决创建

interface Notifier { void send(String msg); }

class SmsNotifier implements Notifier {
    public void send(String msg) { System.out.println("短信: " + msg); }
}
class MailNotifier implements Notifier {
    public void send(String msg) { System.out.println("邮件: " + msg); }
}

class NotifierFactory {
    static Notifier create(String type) {
        switch (type) {
            case "sms":  return new SmsNotifier();
            case "mail": return new MailNotifier();
            default: throw new IllegalArgumentException("未知渠道: " + type);
        }
    }
}

// 调用方:拿到对象后自己调
Notifier n = NotifierFactory.create(config.getChannel());
n.send("订单已发货");

这里没有"算法可切换"的诉求,只是不想让调用方写 new,这就是纯工厂。

4. 工厂 + 策略:各管一段

interface DiscountStrategy {
    long apply(long amount);
    String type();
}

class FullReduction implements DiscountStrategy {
    public long apply(long amount) { return amount >= 100_00 ? amount - 10_00 : amount; }
    public String type() { return "full_reduction"; }
}
class PercentOff implements DiscountStrategy {
    public long apply(long amount) { return amount * 85 / 100; }   // 85 折
    public String type() { return "percent_off"; }
}

// 工厂:负责按类型产出策略,并集中注册
class DiscountFactory {
    private static final Map<String, DiscountStrategy> REG = new HashMap<>();

    static {
        register(new FullReduction());
        register(new PercentOff());
    }
    static void register(DiscountStrategy s) { REG.put(s.type(), s); }

    static DiscountStrategy get(String type) {
        DiscountStrategy s = REG.get(type);
        if (s == null) throw new IllegalArgumentException("未知优惠类型: " + type);
        return s;
    }
}

// 上下文:持有策略,只负责编排
class PriceCalculator {
    private final DiscountStrategy strategy;
    PriceCalculator(String type) { this.strategy = DiscountFactory.get(type); }
    long calc(long amount) { return strategy.apply(amount); }
}

分工很清晰:工厂解决"从字符串到对象"的映射,策略解决"不同算法的具体计算"。缺了工厂,策略的选取逻辑会散落在调用方;缺了策略,工厂造出来的对象只能靠 if 区分行为。

5. 什么场景只用其中一个

场景该用什么理由
产品有复杂构造过程(多参数、需校验)工厂 / 建造者关注创建,不涉及行为切换
同一接口多种算法,运行时按配置切换策略关注行为,创建很简单
多渠道支付:构造客户端 + 不同签名算法工厂 + 策略创建与算法都要隔离

6. 常见追问

Q:Map<String, Strategy> 注册表算工厂还是策略? 它是工厂的变体(简单工厂 + 注册表),产出的对象恰好是策略。判断归属看职责:负责"给一个 key 换一个对象"就是工厂;负责"拿着对象算结果"才是策略。

Q:能不能用枚举 + 函数式接口替代这两个模式? 能覆盖大多数轻量场景:

enum Discount {
    FULL_REDUCTION(a -> a >= 100_00 ? a - 10_00 : a),
    PERCENT_OFF(a -> a * 85 / 100);

    private final LongUnaryOperator op;
    Discount(LongUnaryOperator op) { this.op = op; }
    long apply(long a) { return op.applyAsLong(a); }
}
// Discount.valueOf(type).apply(amount);

枚举天然是"工厂 + 策略"的合体:valueOf 负责按名字取,常量自己带算法。缺点是算法复杂、需要依赖注入时不适合塞进枚举;算法需要访问 DAO 或 RPC 时,仍应回归独立策略类 + Spring 管理。

小结

  • 工厂解决"创建",策略解决"行为替换";结构上工厂的链到产品终止,策略的链在上下文与算法间往返。
  • 工程标配是工厂 + 策略:注册表消 switch,策略类放算法。
  • 轻量场景可用枚举 + 函数式接口一步到位;判断归属只看职责,不看类图。
笔记加载中…