抽象工厂、策略模式和桥接模式有什么区别?
简化版
抽象工厂解决“一组相关产品如何配套创建”,策略模式解决“一个行为算法如何替换”,桥接模式解决“抽象和实现两个维度如何独立变化”。如果问题是创建整套对象,用抽象工厂;如果问题是运行时换算法,用策略;如果问题是两个维度互相组合爆炸,用桥接。
详细版
三者都涉及抽象和替换,因此面试中容易混淆。关键是看变化点:
| 模式 | 主要问题 | 典型例子 |
|---|---|---|
| 抽象工厂 | 创建一组同族产品 | AWS 工厂创建存储、队列、监控 |
| 策略模式 | 替换一个算法行为 | 满减、折扣、会员价算法 |
| 桥接模式 | 两个维度独立变化 | 消息类型和发送渠道分离 |
抽象工厂通常出现在创建阶段,返回多个兼容产品;策略通常出现在行为执行阶段,调用一个统一算法接口;桥接则通过组合把抽象层和实现层拆开,避免 形状 * 颜色、消息类型 * 渠道 这类类爆炸。
它们可以组合使用:抽象工厂创建一组策略,桥接中的实现对象也可能由工厂创建。但答题时先说清各自解决的问题,不要只按“都有接口”混为一谈。
完整版教学
一、先用变化点区分三个模式
设计模式不是按类图长相死记,而是看变化点在哪里。抽象工厂的变化点是产品族,策略模式的变化点是算法行为,桥接模式的变化点是两个可独立变化的维度。
抽象工厂:同一族产品要一起换
策略模式:某一个行为算法要可替换
桥接模式:两个维度都要扩展,不能互相绑定
如果一个问题只需要替换折扣算法,不需要创建一组对象,用策略更直接。如果一个问题需要 AWS 一整套客户端与阿里云一整套客户端切换,抽象工厂更贴切。
二、抽象工厂关注创建一整套产品
抽象工厂的典型结构是一个工厂接口里有多个创建方法,每个具体工厂返回同一产品族的多个产品。
interface CloudFactory {
StorageClient storage();
QueueClient queue();
MonitorClient monitor();
}
它最关心的是产品族一致性。比如选择 AwsFactory 后,storage、queue、monitor 都应来自 AWS。如果只创建一个产品,工厂方法或简单工厂可能足够;如果要保证一组产品配套,抽象工厂才有明显价值。
三、策略模式关注替换一个行为
策略模式通常只有一个行为接口,例如价格计算:
interface DiscountStrategy {
BigDecimal discount(Order order);
}
春节活动、会员活动、新人活动都可以实现这个接口。业务代码在运行时选择一个策略并执行,重点是算法可替换,而不是创建一组兼容对象。
如果系统有 6 种折扣算法,策略模式让业务流程不用写 6 个 if-else。但它不会告诉你如何创建“同一产品族的多个对象”,也不强调产品等级结构。
四、桥接模式关注两个维度拆开
桥接模式用于两个维度都可能扩展时。比如消息有普通消息、紧急消息、营销消息;发送渠道有短信、邮件、企业微信。如果用继承硬组合,类数量会变成:
3 种消息类型 * 3 种发送渠道 = 9 个类
增加到 5 种消息、4 种渠道时就是 5 * 4 = 20 个类。桥接把消息抽象和发送实现拆开:
abstract class Message {
protected Sender sender;
}
interface Sender {
void send(String text);
}
这样消息类型和渠道可以独立扩展,通过组合连接。桥接解决的是维度爆炸,不是产品族创建。
记忆钩子:抽象工厂看“同族创建”,策略看“单点行为”,桥接看“双维解耦”。
五、为什么它们看起来容易混
三者都会出现接口、实现类和组合关系。抽象工厂可能根据配置选择工厂,看起来像策略;桥接中的实现对象可能由工厂创建,看起来又像抽象工厂。混淆通常来自只看代码形态,不看目的。
| 判断问题 | 对应模式 |
|---|---|
| 是否要创建多种配套产品? | 抽象工厂 |
| 是否只是替换一种算法? | 策略 |
| 是否有两个维度独立扩展? | 桥接 |
| 是否需要保证产品族不混搭? | 抽象工厂 |
| 是否需要避免维度组合类爆炸? | 桥接 |
面试时可以先问“变化的是对象族、行为,还是维度组合”。这个问题比背 UML 更有效。
六、三者可以如何组合
实际项目可以组合使用。例如一个多云消息系统:
- 抽象工厂根据云厂商创建一组客户端;
- 策略模式选择消息重试算法;
- 桥接模式拆分消息类型和发送渠道。
CloudFactory -> StorageClient / QueueClient / MonitorClient
RetryStrategy -> FixedRetry / ExponentialBackoff
Message + Sender -> UrgentMessage 组合 SmsSender
组合不是问题,问题是职责不清。如果抽象工厂里塞入大量算法选择,它会变成策略集合;如果策略负责创建一整套资源,它会承担过多创建职责。
七、常见误区与追问
- 误区:只要运行时选择实现就是策略模式。 如果选择的是一整套产品族,更接近抽象工厂。
- 误区:桥接模式就是抽象工厂创建出来的对象组合。 桥接重点是两个维度独立变化,而不是创建过程。
- 误区:抽象工厂也能切换实现,所以和策略没区别。 抽象工厂切换产品族,策略切换一个行为算法。
- 误区:模式可以只看类图判断。 类图相似时必须回到变化点和意图。
- 追问:支付渠道选择应该用哪个? 如果只是支付算法或通道行为,用策略;如果每个渠道还要配套创建签名器、客户端、回调解析器,可用抽象工厂。
- 追问:消息类型和发送渠道组合爆炸用哪个? 桥接更合适,它把两个维度拆开。
- 追问:抽象工厂能否创建策略对象? 可以,但那是组合使用,不改变两种模式的意图差异。
八、加强记忆
这题用“三问法”记:是不是一整套产品要配齐,是抽象工厂;是不是一个行为要替换,是策略;是不是两个维度要独立扩展,是桥接。先判断变化点,再谈类结构,答案会更稳。