1. 工厂模式与策略模式实战解析
最近在重构一个老项目时,我频繁用到了工厂模式和策略模式这两种设计模式。作为面向对象编程中最常用的两种模式,它们能有效解决代码中复杂的对象创建和算法选择问题。今天我就结合自己踩过的坑,聊聊这两种模式的核心用法和实际应用场景。
工厂模式就像个智能生产线,根据你的需求自动创建合适的对象;而策略模式则像个万能工具箱,让你能随时切换不同的算法实现。在电商促销系统、支付网关对接这些典型场景里,这两种模式经常配合使用。比如双十一时要根据不同的促销规则计算折扣,用策略模式封装算法,再用工厂模式按需创建策略实例,代码会清晰很多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 工厂模式深度剖析
2.1 简单工厂的经典实现
先看一个电商折扣计算的例子。假设我们有三种折扣类型:普通用户折扣、VIP折扣和节日特惠折扣。用简单工厂可以这样实现:
java复制public interface Discount {
BigDecimal apply(BigDecimal originalPrice);
}
public class RegularDiscount implements Discount {
@Override
public BigDecimal apply(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.95"));
}
}
public class VipDiscount implements Discount {
@Override
public BigDecimal apply(BigDecimal originalPrice) {
return originalPrice.multiply(new BigDecimal("0.85"));
}
}
public class DiscountFactory {
public static Discount createDiscount(String type) {
switch (type) {
case "VIP":
return new VipDiscount();
case "FESTIVAL":
return new FestivalDiscount();
default:
return new RegularDiscount();
}
}
}
这样使用时只需要:
java复制Discount discount = DiscountFactory.createDiscount("VIP");
BigDecimal finalPrice = discount.apply(originalPrice);
注意:简单工厂虽然方便,但违反开闭原则。新增折扣类型时需要修改工厂类的create方法。在实际项目中,如果折扣类型频繁变化,建议改用工厂方法模式。
2.2 工厂方法模式进阶
工厂方法模式通过引入抽象工厂解决了简单工厂的扩展问题:
java复制public abstract class DiscountFactory {
public abstract Discount createDiscount();
// 可以包含一些公共逻辑
public BigDecimal calculateFinalPrice(BigDecimal originalPrice) {
Discount discount = createDiscount();
return discount.apply(originalPrice);
}
}
public class VipDiscountFactory extends DiscountFactory {
@Override
public Discount createDiscount() {
return new VipDiscount();
}
}
使用方式:
java复制DiscountFactory factory = new VipDiscountFactory();
BigDecimal finalPrice = factory.calculateFinalPrice(originalPrice);
这种方式的优势在于:
- 符合开闭原则,新增折扣类型只需新增工厂类
- 工厂类可以包含业务逻辑,而不仅仅是创建对象
- 子类可以决定具体创建哪种对象
2.3 抽象工厂模式实战
当需要创建一系列相关对象时,抽象工厂就派上用场了。比如电商系统中的完整订单服务:
java复制public interface OrderFactory {
Discount createDiscount();
Shipping createShipping();
Invoice createInvoice();
}
public class VipOrderFactory implements OrderFactory {
@Override public Discount createDiscount() { return new VipDiscount(); }
@Override public Shipping createShipping() { return new ExpressShipping(); }
@Override public Invoice createInvoice() { return new VipInvoice(); }
}
3. 策略模式核心应用
3.1 基础策略模式实现
继续用折扣计算的例子,策略模式可以这样实现:
java复制public interface DiscountStrategy {
BigDecimal apply(BigDecimal originalPrice);
}
public class Context {
private DiscountStrategy strategy;
public Context(DiscountStrategy strategy) {
this.strategy = strategy;
}
public BigDecimal executeStrategy(BigDecimal price) {
return strategy.apply(price);
}
// 支持运行时切换策略
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
}
使用示例:
java复制Context context = new Context(new VipDiscountStrategy());
BigDecimal finalPrice = context.executeStrategy(originalPrice);
3.2 策略模式+枚举的高级用法
结合枚举可以让代码更优雅:
java复制public enum DiscountType {
REGULAR(new RegularDiscount()),
VIP(new VipDiscount()),
FESTIVAL(new FestivalDiscount());
private final DiscountStrategy strategy;
DiscountType(DiscountStrategy strategy) {
this.strategy = strategy;
}
public BigDecimal apply(BigDecimal price) {
return strategy.apply(price);
}
}
使用方式极其简洁:
java复制BigDecimal finalPrice = DiscountType.VIP.apply(originalPrice);
3.3 策略模式与Spring整合
在Spring项目中,可以这样管理策略:
java复制@Service
public class DiscountService {
private Map<String, DiscountStrategy> strategies;
// Spring会自动注入所有DiscountStrategy实现
@Autowired
public DiscountService(List<DiscountStrategy> strategyList) {
strategies = strategyList.stream()
.collect(Collectors.toMap(
s -> s.getClass().getAnnotation(DiscountTypeAnnotation.class).value(),
Function.identity()
));
}
public BigDecimal applyDiscount(String type, BigDecimal price) {
return strategies.get(type).apply(price);
}
}
4. 工厂模式与策略模式联合实战
4.1 支付网关集成案例
在支付系统开发中,我们通常需要对接多个支付渠道(支付宝、微信、银联等)。每个渠道的支付流程、退款流程都不同。这时候就可以:
- 用工厂模式创建具体的支付处理器
- 用策略模式封装不同的支付算法
java复制public interface PaymentStrategy {
PaymentResult pay(PaymentRequest request);
RefundResult refund(RefundRequest request);
}
public class PaymentFactory {
public static PaymentStrategy create(String channel) {
switch (channel) {
case "ALIPAY": return new AlipayStrategy();
case "WECHAT": return new WechatStrategy();
default: throw new IllegalArgumentException("Unsupported channel");
}
}
}
4.2 动态策略加载技巧
通过反射可以实现更灵活的策略加载:
java复制public class StrategyFactory {
private static final String STRATEGY_PACKAGE = "com.example.strategy";
public static DiscountStrategy create(String strategyName) {
try {
Class<?> clazz = Class.forName(STRATEGY_PACKAGE + "." + strategyName);
return (DiscountStrategy) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Strategy load failed", e);
}
}
}
警告:反射虽然强大,但会带来性能开销和安全风险。生产环境使用时建议:
- 缓存Class对象
- 做好异常处理
- 限制可加载的包路径
5. 常见问题与性能优化
5.1 模式选择困惑
很多开发者分不清什么时候用工厂模式,什么时候用策略模式。其实记住:
- 工厂关注对象创建
- 策略关注算法替换
如果主要问题是"如何创建对象",用工厂;如果是"如何灵活切换算法",用策略。
5.2 策略模式性能优化
当策略实现非常轻量时,频繁创建策略实例可能成为性能瓶颈。这时可以:
- 使用枚举实现单例策略
- 结合对象池复用策略实例
- 对于无状态策略,始终使用同一个实例
5.3 工厂模式线程安全
静态工厂方法在多线程环境下需要注意:
java复制public class SingletonFactory {
private static volatile SomeObject instance;
public static SomeObject getInstance() {
if (instance == null) {
synchronized (SingletonFactory.class) {
if (instance == null) {
instance = new SomeObject();
}
}
}
return instance;
}
}
5.4 Spring中的最佳实践
在Spring项目中,更推荐使用依赖注入而非手动创建工厂:
java复制@Service
public class OrderService {
private final Map<String, DiscountStrategy> strategies;
@Autowired
public OrderService(List<DiscountStrategy> strategyList) {
this.strategies = strategyList.stream()
.collect(Collectors.toMap(
s -> s.getClass().getSimpleName(),
Function.identity()
));
}
public void applyDiscount(String strategyName, Order order) {
DiscountStrategy strategy = strategies.get(strategyName);
if (strategy != null) {
order.setFinalPrice(strategy.apply(order.getOriginalPrice()));
}
}
}
6. 设计模式组合创新
6.1 策略+模板方法模式
当多个策略有共同流程时,可以结合模板方法模式:
java复制public abstract class AbstractDiscountStrategy implements DiscountStrategy {
// 模板方法定义算法骨架
public final BigDecimal apply(BigDecimal price) {
validate(price);
BigDecimal discounted = doApply(price);
logResult(discounted);
return discounted;
}
protected abstract BigDecimal doApply(BigDecimal price);
private void validate(BigDecimal price) {
if (price.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Price must be positive");
}
}
private void logResult(BigDecimal result) {
System.out.println("Discount applied: " + result);
}
}
6.2 工厂+建造者模式
复杂对象创建可以结合建造者模式:
java复制public class ReportFactory {
public Report createReport(ReportType type, ReportBuilder builder) {
switch (type) {
case PDF:
return builder.setHeader("PDF Header")
.setFooter("PDF Footer")
.setFormat(Format.PDF)
.build();
case EXCEL:
return builder.setHeader("Excel Header")
.setFormat(Format.EXCEL)
.build();
default:
throw new IllegalArgumentException("Unsupported report type");
}
}
}
在实际项目中,我经常遇到需要动态加载策略的场景。比如最近做的风控系统,需要根据不同的风险等级应用不同的验证策略。最终我的解决方案是:用工厂模式创建策略上下文,用策略模式封装具体算法,再结合Spring的@Conditional注解实现策略的自动装配。这样当新增风险等级时,只需要实现新的策略类并加上相应注解即可,完全符合开闭原则。
