一、为什么设计模式是复杂业务的解药

咱们程序员平时写代码,最怕遇到什么?不是技术难题,而是业务逻辑像毛线团一样越缠越乱。特别是当需求三天一小改,五天一大变的时候,昨天刚写好的代码今天就得推倒重来。这时候,设计模式就像是一把瑞士军刀,能帮我们优雅地解决问题。

举个电商系统的例子。假设我们要实现一个订单折扣系统,初期可能只有会员折扣,后来要加满减活动,接着又来了优惠券。如果一开始就用策略模式,后面加功能就像搭积木一样简单:

// 技术栈:Java 17
// 策略模式实现折扣计算
public interface DiscountStrategy {
    BigDecimal applyDiscount(Order order);
}

// 会员折扣实现
public class MemberDiscount implements DiscountStrategy {
    @Override
    public BigDecimal applyDiscount(Order order) {
        return order.getTotal().multiply(BigDecimal.valueOf(0.9));
    }
}

// 满减实现
public class FullReductionDiscount implements DiscountStrategy {
    @Override
    public BigDecimal applyDiscount(Order order) {
        if(order.getTotal().compareTo(BigDecimal.valueOf(200)) > 0) {
            return order.getTotal().subtract(BigDecimal.valueOf(20));
        }
        return order.getTotal();
    }
}

// 使用示例
public class DiscountService {
    private List<DiscountStrategy> strategies;
    
    public BigDecimal calculateFinalPrice(Order order) {
        BigDecimal result = order.getTotal();
        for(DiscountStrategy strategy : strategies) {
            result = strategy.applyDiscount(order);
        }
        return result;
    }
}

你看,这样设计后,新增折扣类型只需要实现新的策略类,完全不用动原有代码。这就是设计模式的魅力 - 把变化隔离在最小范围内。

二、工厂模式在对象创建中的妙用

说到创建对象,新手最容易犯的错误就是到处new对象。等需要换实现类的时候,得改几十个地方。这时候工厂模式就派上用场了。

比如我们有个文件导出功能,要支持PDF、Excel、CSV三种格式。用简单工厂模式可以这样实现:

// 技术栈:Java 17 + Apache POI
public interface Exporter {
    void export(ReportData data, OutputStream output);
}

public class PdfExporter implements Exporter {
    @Override
    public void export(ReportData data, OutputStream output) {
        // 使用iText等库实现PDF导出
    }
}

public class ExcelExporter implements Exporter {
    @Override
    public void export(ReportData data, OutputStream output) {
        // 使用POI实现Excel导出
    }
}

public class ExporterFactory {
    public static Exporter createExporter(ExportType type) {
        switch(type) {
            case PDF: return new PdfExporter();
            case EXCEL: return new ExcelExporter();
            case CSV: return new CsvExporter();
            default: throw new IllegalArgumentException("不支持的导出类型");
        }
    }
}

// 使用示例
public class ExportService {
    public void exportReport(ExportType type, ReportData data) {
        Exporter exporter = ExporterFactory.createExporter(type);
        try(OutputStream out = new FileOutputStream("report." + type.getExt())) {
            exporter.export(data, out);
        }
    }
}

工厂模式的好处是:

  1. 创建逻辑集中管理
  2. 客户端代码与具体实现解耦
  3. 新增导出类型时,只需扩展工厂类

三、观察者模式处理事件驱动场景

现代应用越来越依赖事件驱动架构。比如订单状态变化时要通知物流系统、发短信、更新库存等。如果直接在订单服务里写这些逻辑,代码会变成一锅粥。

观察者模式可以优雅地解决这个问题:

// 技术栈:Java 17
// 事件定义
public class OrderEvent {
    private Order order;
    private EventType type;
    // 省略getter/setter
}

// 观察者接口
public interface OrderListener {
    void onEvent(OrderEvent event);
}

// 具体观察者:库存服务
public class InventoryService implements OrderListener {
    @Override
    public void onEvent(OrderEvent event) {
        if(event.getType() == EventType.PAID) {
            updateStock(event.getOrder().getItems());
        }
    }
}

// 主题(被观察者)
public class OrderService {
    private List<OrderListener> listeners = new ArrayList<>();
    
    public void addListener(OrderListener listener) {
        listeners.add(listener);
    }
    
    public void payOrder(Order order) {
        // 支付逻辑...
        notifyListeners(new OrderEvent(order, EventType.PAID));
    }
    
    private void notifyListeners(OrderEvent event) {
        listeners.forEach(listener -> listener.onEvent(event));
    }
}

// 使用示例
public class Application {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        orderService.addListener(new InventoryService());
        orderService.addListener(new SmsService());
        
        Order order = new Order();
        orderService.payOrder(order);
    }
}

这个模式让各个服务之间松耦合,新加监听器完全不影响现有代码。Java自带的java.util.Observable就是基于这个模式,但在实际项目中我们更推荐自己实现,灵活性更高。

四、装饰器模式实现动态扩展

有时候我们需要在运行时动态地给对象添加功能。继承是静态的,而且容易导致类爆炸。装饰器模式提供了更灵活的方案。

举个IO流的例子。我们要实现一个带缓存的、带加密的文件读取功能:

// 技术栈:Java 17
public interface DataSource {
    void writeData(byte[] data);
    byte[] readData();
}

// 基础实现
public class FileDataSource implements DataSource {
    private String filename;
    
    @Override
    public void writeData(byte[] data) {
        // 基础文件写入
    }
    
    @Override
    public byte[] readData() {
        // 基础文件读取
        return new byte[0];
    }
}

// 装饰器基类
public abstract class DataSourceDecorator implements DataSource {
    protected DataSource wrappee;
    
    public DataSourceDecorator(DataSource source) {
        this.wrappee = source;
    }
}

// 加密装饰器
public class EncryptionDecorator extends DataSourceDecorator {
    public EncryptionDecorator(DataSource source) {
        super(source);
    }
    
    @Override
    public void writeData(byte[] data) {
        byte[] encrypted = encrypt(data);
        wrappee.writeData(encrypted);
    }
    
    @Override
    public byte[] readData() {
        byte[] data = wrappee.readData();
        return decrypt(data);
    }
    
    private byte[] encrypt(byte[] data) {
        // 加密实现
        return data;
    }
    
    private byte[] decrypt(byte[] data) {
        // 解密实现
        return data;
    }
}

// 使用示例
public class Client {
    public static void main(String[] args) {
        DataSource source = new FileDataSource("data.txt");
        source = new EncryptionDecorator(source);
        source = new BufferingDecorator(source);
        
        source.writeData("Hello".getBytes());
    }
}

装饰器模式的特点:

  1. 比继承更灵活
  2. 可以动态添加/移除功能
  3. 保持单一职责原则
  4. Java IO流就是经典应用

五、设计模式组合使用的威力

实际项目中,我们经常需要组合使用多种设计模式。比如电商的促销系统:

// 技术栈:Java 17 + Spring
// 策略模式定义促销计算
public interface PromotionStrategy {
    PromotionResult calculate(PromotionContext context);
}

// 工厂模式创建策略
public class StrategyFactory {
    public static PromotionStrategy getStrategy(PromotionType type) {
        // 返回具体策略
    }
}

// 装饰器模式添加日志
public class LoggingDecorator implements PromotionStrategy {
    private PromotionStrategy strategy;
    
    @Override
    public PromotionResult calculate(PromotionContext context) {
        log("开始计算促销");
        PromotionResult result = strategy.calculate(context);
        log("促销计算完成");
        return result;
    }
}

// 观察者模式处理促销事件
public class PromotionEventPublisher {
    private List<PromotionListener> listeners;
    
    public void publish(PromotionEvent event) {
        listeners.forEach(l -> l.onEvent(event));
    }
}

// 使用示例
public class PromotionService {
    public PromotionResult applyPromotion(PromotionRequest request) {
        PromotionStrategy strategy = StrategyFactory.getStrategy(request.getType());
        strategy = new LoggingDecorator(strategy);
        
        PromotionResult result = strategy.calculate(request.getContext());
        
        if(result.isSuccess()) {
            eventPublisher.publish(new PromotionEvent(result));
        }
        
        return result;
    }
}

这种组合拳打法让系统:

  1. 计算逻辑可灵活替换
  2. 创建过程统一管理
  3. 功能可动态扩展
  4. 事件处理解耦

六、设计模式应用的注意事项

虽然设计模式很强大,但也要注意几个坑:

  1. 不要过度设计 - 简单业务用简单方法
  2. 模式不是银弹 - 要结合具体场景
  3. 注意性能开销 - 比如装饰器模式会增加调用层级
  4. 团队共识很重要 - 大家都得理解设计意图
  5. 文档和注释要跟上 - 方便后续维护

举个例子,我们曾经有个项目滥用观察者模式,导致一个事件有20多个监听器,调试起来像走迷宫。后来改用领域事件+消息队列才解决。

七、总结

设计模式就像编程中的成语,用得好能让代码更优雅。但记住:

  • 理解比死记硬背重要
  • 组合使用比单打独斗有效
  • 适合的才是最好的
  • 重构是持续的过程

下次面对复杂业务时,不妨先想想:这个场景适合什么模式?你会发现代码质量会有质的飞跃。