一、为什么设计模式是复杂业务的解药
咱们程序员平时写代码,最怕遇到什么?不是技术难题,而是业务逻辑像毛线团一样越缠越乱。特别是当需求三天一小改,五天一大变的时候,昨天刚写好的代码今天就得推倒重来。这时候,设计模式就像是一把瑞士军刀,能帮我们优雅地解决问题。
举个电商系统的例子。假设我们要实现一个订单折扣系统,初期可能只有会员折扣,后来要加满减活动,接着又来了优惠券。如果一开始就用策略模式,后面加功能就像搭积木一样简单:
// 技术栈: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);
}
}
}
工厂模式的好处是:
- 创建逻辑集中管理
- 客户端代码与具体实现解耦
- 新增导出类型时,只需扩展工厂类
三、观察者模式处理事件驱动场景
现代应用越来越依赖事件驱动架构。比如订单状态变化时要通知物流系统、发短信、更新库存等。如果直接在订单服务里写这些逻辑,代码会变成一锅粥。
观察者模式可以优雅地解决这个问题:
// 技术栈: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());
}
}
装饰器模式的特点:
- 比继承更灵活
- 可以动态添加/移除功能
- 保持单一职责原则
- 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;
}
}
这种组合拳打法让系统:
- 计算逻辑可灵活替换
- 创建过程统一管理
- 功能可动态扩展
- 事件处理解耦
六、设计模式应用的注意事项
虽然设计模式很强大,但也要注意几个坑:
- 不要过度设计 - 简单业务用简单方法
- 模式不是银弹 - 要结合具体场景
- 注意性能开销 - 比如装饰器模式会增加调用层级
- 团队共识很重要 - 大家都得理解设计意图
- 文档和注释要跟上 - 方便后续维护
举个例子,我们曾经有个项目滥用观察者模式,导致一个事件有20多个监听器,调试起来像走迷宫。后来改用领域事件+消息队列才解决。
七、总结
设计模式就像编程中的成语,用得好能让代码更优雅。但记住:
- 理解比死记硬背重要
- 组合使用比单打独斗有效
- 适合的才是最好的
- 重构是持续的过程
下次面对复杂业务时,不妨先想想:这个场景适合什么模式?你会发现代码质量会有质的飞跃。
评论