SpringBoot中设计模式组合使用示例-策略、模板、观察者、门面、工厂、单例。

SpringBoot中设计模式组合使用示例-策略、模板、观察者、门面、工厂、单例。


注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

一、策略模式(Strategy Pattern)

概念

定义一组算法/行为,将每个算法封装成独立的处理单元,使它们可以互相替换。客户端根据条件选择使用哪个策略。

解决的问题

避免大量的 if-elseswitch-case 散落在业务逻辑中,新增策略时不需要修改已有代码。

项目中的体现

根据物流节点编码路由到不同处理方法:

java 复制代码
switch (OpCodeEnums.valueOfCode(paramsDto.getOpCode())) {
    case a:                    this.aHandle(...); break;
    case b:                    this.bHandle(...); break;
    case c:   this.cHandle(...); break;
    // ...
}

每个 case 分支对应一个独立的处理策略。新增事件只需添加一个 case 和对应方法。

通用代码示例

java 复制代码
/**
 * 策略接口:定义统一的处理行为.
 */
public interface PaymentStrategy {
    /** 执行支付. */
    PayResult pay(PayRequest request);
    /** 是否支持该支付方式. */
    boolean support(String payType);
}

/**
 * 具体策略:微信支付.
 */
@Component
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public PayResult pay(PayRequest request) {
        // 调用微信支付 API
        return new PayResult(true, "微信支付成功");
    }

    @Override
    public boolean support(String payType) {
        return "WECHAT".equals(payType);
    }
}

/**
 * 具体策略:支付宝支付.
 */
@Component
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public PayResult pay(PayRequest request) {
        // 调用支付宝 API
        return new PayResult(true, "支付宝支付成功");
    }

    @Override
    public boolean support(String payType) {
        return "ALIPAY".equals(payType);
    }
}

/**
 * 策略上下文:根据条件选择策略并执行.
 * Spring 自动注入所有 PaymentStrategy 实现.
 */
@Service
public class PaymentService {

    @Resource
    private List<PaymentStrategy> strategies;

    public PayResult pay(String payType, PayRequest request) {
        PaymentStrategy strategy = strategies.stream()
            .filter(s -> s.support(payType))
            .findFirst()
            .orElseThrow(() -> new RuntimeException("不支持的支付方式: " + payType));
        return strategy.pay(request);
    }
}

类图

复制代码
┌────────────────────┐
│  PaymentService    │ (上下文,选择策略)
│  - strategies      │
│  + pay()           │
└────────┬───────────┘
         │ 持有
         ▼
┌────────────────────┐
│ <<interface>>      │
│ PaymentStrategy    │
│ + pay()            │
│ + support()        │
└────────┬───────────┘
         │ 实现
    ┌────┴────┐
    ▼         ▼
┌────────┐ ┌────────┐
│Wechat  │ │Alipay  │
│Strategy│ │Strategy│
└────────┘ └────────┘

二、模板方法模式(Template Method Pattern)

概念

在父类中定义一个算法的骨架(步骤顺序),将某些步骤的具体实现延迟到子类或通过参数差异化。整体流程不变,只是个别步骤的行为不同。

解决的问题

多个流程整体结构相同,只有部分细节不同。避免重复代码,把共同逻辑抽到一处。

项目中的体现

方法是一个固定模板,不同事件(a/b/c)走同一套流程:

复制代码
① 判断是否指定类型订单       → 所有事件都一样
② 构造 DTO              → event 字段不同
③ 写日志表              → 所有事件都一样
④ 注册事务回调发MQ       → 所有事件都一样
⑤ 调用标签推送          → 根据 event 设置不同标签

调用方只需传入不同的 event 参数:

java 复制代码
aaService.interceptNodeTobb(data, recordItem, "a");  
aasService.interceptNodeTobb(data, recordItem, "b");  
aasService.interceptNodeTobb(data, recordItem, "c"); 

通用代码示例

java 复制代码
/**
 * 抽象模板类:定义导出流程骨架.
 */
public abstract class AbstractExportTemplate {

    /**
     * 模板方法:固定的执行步骤.
     * final 防止子类覆盖整体流程.
     */
    public final void export(ExportRequest request) {
        // 步骤1:校验参数(通用)
        validate(request);
        // 步骤2:查询数据(子类实现)
        List<Object> data = queryData(request);
        // 步骤3:转换格式(子类实现)
        byte[] content = convertToFile(data, request.getFileType());
        // 步骤4:上传文件(通用)
        upload(content, request.getFileName());
        // 步骤5:发送通知(通用)
        notify(request.getUserId(), request.getFileName());
    }

    private void validate(ExportRequest request) {
        if (request.getUserId() == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
    }

    /** 子类实现:查询数据. */
    protected abstract List<Object> queryData(ExportRequest request);

    /** 子类实现:转换为文件内容. */
    protected abstract byte[] convertToFile(List<Object> data, String fileType);

    private void upload(byte[] content, String fileName) {
        // 上传到 OSS
    }

    private void notify(Integer userId, String fileName) {
        // 发送消息通知用户
    }
}

/**
 * 具体实现:订单导出.
 */
@Service
public class OrderExportService extends AbstractExportTemplate {

    @Override
    protected List<Object> queryData(ExportRequest request) {
        return orderRepository.findByCondition(request.getCondition());
    }

    @Override
    protected byte[] convertToFile(List<Object> data, String fileType) {
        if ("excel".equals(fileType)) {
            return ExcelUtil.toExcel(data);
        }
        return CsvUtil.toCsv(data);
    }
}

/**
 * 具体实现:库存导出.
 */
@Service
public class StockExportService extends AbstractExportTemplate {

    @Override
    protected List<Object> queryData(ExportRequest request) {
        return stockRepository.findByWarehouseId(request.getWarehouseId());
    }

    @Override
    protected byte[] convertToFile(List<Object> data, String fileType) {
        return ExcelUtil.toExcel(data);
    }
}

类图

复制代码
┌──────────────────────────────┐
│ AbstractExportTemplate       │
│                              │
│ + export() [final, 模板方法]  │
│ - validate()                 │
│ # queryData() [abstract]     │
│ # convertToFile() [abstract] │
│ - upload()                   │
│ - notify()                   │
└──────────────┬───────────────┘
               │ 继承
        ┌──────┴──────┐
        ▼             ▼
┌──────────────┐ ┌──────────────┐
│OrderExport   │ │StockExport   │
│Service       │ │Service       │
└──────────────┘ └──────────────┘

三、观察者模式(Observer Pattern)

概念

定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都得到通知并自动更新。

解决的问题

一件事发生后需要触发多个后续动作,但不想把这些动作硬编码在主流程中。

项目中的体现

Spring 的 TransactionSynchronizationManager 就是观察者模式的应用:

java 复制代码
// 事务是被观察的"主题"
// AfterTransactionActionCollector 是"观察者"
// 事务提交这个"事件"触发观察者执行
AfterTransactionActionCollector collector = new AfterTransactionActionCollector();
TransactionSynchronizationManager.registerSynchronization(collector);
collector.addCommitSyncAction(() -> sendJstMessageMqSender.sendJstMessageMq(recordLog.getId()));

事务提交后,所有注册的同步回调自动被触发执行。

通用代码示例

方式一:Spring 事件机制
java 复制代码
/**
 * 事件定义:订单创建事件.
 */
public class OrderCreatedEvent extends ApplicationEvent {
    private final String orderCode;
    private final Integer userId;

    public OrderCreatedEvent(Object source, String orderCode, Integer userId) {
        super(source);
        this.orderCode = orderCode;
        this.userId = userId;
    }

    public String getOrderCode() { return orderCode; }
    public Integer getUserId() { return userId; }
}

/**
 * 事件发布者:订单服务.
 * 只负责发布事件,不关心谁在监听.
 */
@Service
public class OrderService {

    @Resource
    private ApplicationEventPublisher eventPublisher;

    @Transactional
    public void createOrder(OrderDto dto) {
        // 核心业务:创建订单
        Order order = orderRepository.save(buildOrder(dto));

        // 发布事件(不关心后续处理)
        eventPublisher.publishEvent(
            new OrderCreatedEvent(this, order.getOrderCode(), dto.getUserId()));
    }
}

/**
 * 观察者1:发送短信通知.
 */
@Component
public class SmsNotificationListener {

    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        smsService.send(event.getUserId(), "您的订单已创建:" + event.getOrderCode());
    }
}

/**
 * 观察者2:初始化物流信息.
 */
@Component
public class LogisticsInitListener {

    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        logisticsService.initLogisticsRecord(event.getOrderCode());
    }
}

/**
 * 观察者3:异步更新统计报表.
 */
@Component
public class ReportListener {

    @Async
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        reportService.incrementDailyCount();
    }
}
方式二:事务提交后回调
java 复制代码
/**
 * 事务提交后触发动作.
 * 确保数据库操作成功后才执行副作用(发MQ、调接口等).
 */
@Service
public class InventoryService {

    public void deductStock(Integer skuId, Integer quantity) {
        // 扣减库存(数据库操作)
        stockRepository.deduct(skuId, quantity);

        // 注册事务提交后的回调
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 事务成功后才通知下游
                    mqSender.sendStockChanged(skuId);
                    cacheService.invalidate("stock:" + skuId);
                }

                @Override
                public void afterCompletion(int status) {
                    if (status == STATUS_ROLLED_BACK) {
                        log.warn("事务回滚,不发送通知");
                    }
                }
            }
        );
    }
}

类图

复制代码
┌───────────────────────┐
│ ApplicationEvent      │ (事件)
│ Publisher             │
│ + publishEvent()      │
└───────────┬───────────┘
            │ 发布
            ▼
┌───────────────────────┐       ┌─────────────────┐
│ OrderCreatedEvent     │──────→│ SmsListener     │ 观察者1
└───────────────────────┘  通知  ├─────────────────┤
                           ────→│ LogisticsListener│ 观察者2
                           ────→│ ReportListener  │ 观察者3
                                └─────────────────┘

四、门面模式(Facade Pattern)

概念

为复杂子系统提供一个简化的统一接口。调用方不需要了解子系统内部的复杂性,只需通过门面接口完成操作。

解决的问题

子系统内部有多个类、多个步骤协作,直接暴露给调用方会导致耦合度高、使用复杂。

项目中的体现

aaService 接口就是门面------封装了与 bb 交互的所有复杂逻辑:

java 复制代码
// 调用方一行代码搞定
aaService.interceptNodeTobb(data, recordItem, "a");

内部实际做了 5 件事:

  1. 查询是否 指定类型订单(调远程 Feign)
  2. 构造 DTO
  3. 写数据库日志表
  4. 注册事务同步回调
  5. 发 MQ

调用方无需知道这些细节。

通用代码示例

java 复制代码
/**
 * 门面接口:对外暴露的简化接口.
 */
public interface OrderFacade {
    /** 一键下单(内部协调多个子系统). */
    OrderResult placeOrder(PlaceOrderRequest request);
}

/**
 * 门面实现:协调多个子系统完成复杂操作.
 */
@Service
@Slf4j
public class OrderFacadeImpl implements OrderFacade {

    @Resource private InventoryService inventoryService;     // 库存子系统
    @Resource private PaymentService paymentService;         // 支付子系统
    @Resource private LogisticsService logisticsService;     // 物流子系统
    @Resource private NotificationService notificationService; // 通知子系统

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderResult placeOrder(PlaceOrderRequest request) {
        // 1. 检查库存
        boolean hasStock = inventoryService.checkAndLock(
            request.getSkuId(), request.getQuantity());
        if (!hasStock) {
            return OrderResult.fail("库存不足");
        }

        // 2. 创建支付单
        String payNo = paymentService.createPayOrder(
            request.getUserId(), request.getAmount());

        // 3. 预分配物流
        logisticsService.preAllocate(request.getAddress());

        // 4. 发送通知
        notificationService.sendOrderCreated(request.getUserId());

        return OrderResult.success(payNo);
    }
}

/**
 * 调用方:只需面对门面接口,不关心内部有哪些子系统.
 */
@RestController
public class OrderController {

    @Resource
    private OrderFacade orderFacade;

    @PostMapping("/order/place")
    public Result<OrderResult> placeOrder(@RequestBody PlaceOrderRequest request) {
        return Result.success(orderFacade.placeOrder(request));
    }
}

类图

复制代码
┌────────────┐          ┌─────────────────────────────────────┐
│ Controller │          │        OrderFacade (门面)            │
│            │─────────→│                                     │
└────────────┘          │  内部协调:                          │
                        │  ┌─────────────┐ ┌───────────────┐ │
                        │  │ Inventory   │ │ Payment       │ │
                        │  │ Service     │ │ Service       │ │
                        │  └─────────────┘ └───────────────┘ │
                        │  ┌─────────────┐ ┌───────────────┐ │
                        │  │ Logistics   │ │ Notification  │ │
                        │  │ Service     │ │ Service       │ │
                        │  └─────────────┘ └───────────────┘ │
                        └─────────────────────────────────────┘

五、工厂模式(Factory Pattern)

概念

将对象的创建逻辑封装到工厂类中,调用方不直接 new 对象,而是通过工厂获取。

解决的问题

对象创建逻辑复杂,或需要根据条件创建不同类型的对象。

项目中的体现

Feign 的 FallbackFactory 就是工厂模式------根据异常类型创建不同的降级实例:

java 复制代码
@FeignClient(fallbackFactory = aaaOrderFallbackFactory.class)
public interface aaaOrderFeign { ... }

FallbackFactory 接口:

java 复制代码
public interface FallbackFactory<T> {
    T create(Throwable cause);  // 工厂方法:根据异常创建降级实例
}

通用代码示例

java 复制代码
/**
 * 产品接口:消息发送器.
 */
public interface MessageSender {
    void send(String target, String content);
}

/**
 * 具体产品:短信发送.
 */
@Component("SMS")
public class SmsSender implements MessageSender {
    @Override
    public void send(String target, String content) {
        // 调用短信平台 API
    }
}

/**
 * 具体产品:邮件发送.
 */
@Component("EMAIL")
public class EmailSender implements MessageSender {
    @Override
    public void send(String target, String content) {
        // 调用邮件服务
    }
}

/**
 * 具体产品:站内信.
 */
@Component("INNER_MSG")
public class InnerMsgSender implements MessageSender {
    @Override
    public void send(String target, String content) {
        // 写入消息表
    }
}

/**
 * 工厂类:根据类型获取对应的发送器.
 * 利用 Spring 的 Map 注入自动收集所有实现.
 */
@Component
public class MessageSenderFactory {

    /** Spring 自动按 Bean 名称注入到 Map 中. */
    @Resource
    private Map<String, MessageSender> senderMap;

    public MessageSender getSender(String type) {
        MessageSender sender = senderMap.get(type);
        if (sender == null) {
            throw new RuntimeException("不支持的消息类型: " + type);
        }
        return sender;
    }
}

/**
 * 使用示例.
 */
@Service
public class NoticeService {

    @Resource
    private MessageSenderFactory factory;

    public void sendNotice(String type, String target, String content) {
        MessageSender sender = factory.getSender(type);
        sender.send(target, content);
    }
}

六、代理模式(Proxy Pattern)

概念

为其他对象提供一种代理,以控制对这个对象的访问。代理对象在调用真实对象前后可以加入额外逻辑。

解决的问题

在不修改原有逻辑的前提下增加横切关注点(日志、鉴权、缓存、重试等)。

项目中的体现

  1. Feign 本身就是动态代理------你写的是接口,Feign 通过 JDK 动态代理生成实现类
  2. Spring AOP(@Transactional)------事务管理通过代理实现
  3. FallbackFactory------代理原始 Feign 接口,在调用失败时走降级逻辑
java 复制代码
// 你写的只是接口
@FeignClient(value = "aaa-order")
public interface aaaOrderFeign {
    @PostMapping("/aaa/order/result/receive")
    RestControllerResult<Boolean> noticeAa(@RequestBody aaOperationDto param);
}

// Feign 框架在运行时生成代理类,大致逻辑:

通用代码示例

java 复制代码
/**
 * 接口定义.
 */
public interface DataService {
    String getData(String key);
}

/**
 * 真实实现:从数据库查询.
 */
@Service("dataServiceImpl")
public class DataServiceImpl implements DataService {
    @Override
    public String getData(String key) {
        // 模拟耗时的数据库查询
        return database.query(key);
    }
}

/**
 * 代理实现:增加缓存逻辑.
 * 先查缓存,缓存没有再调真实实现.
 */
@Service
@Primary
public class CachedDataServiceProxy implements DataService {

    @Resource
    @Qualifier("dataServiceImpl")
    private DataService realService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public String getData(String key) {
        // 先查缓存
        String cached = redisTemplate.opsForValue().get("cache:" + key);
        if (cached != null) {
            return cached;
        }
        // 缓存未命中,调用真实实现
        String data = realService.getData(key);
        // 写入缓存
        redisTemplate.opsForValue().set("cache:" + key, data, 5, TimeUnit.MINUTES);
        return data;
    }
}

七、单例模式(Singleton Pattern)

概念

确保一个类只有一个实例,并提供全局访问点。

项目中的体现

Spring Bean 默认就是单例的。项目中所有 @Service@Component 注解的类在容器中只有一个实例:

java 复制代码
@Service
public class OmsServiceImpl implements OmsService { ... }  // 单例

@Component
public class EventCenterUtil { ... }  // 单例

通用代码示例

java 复制代码
/**
 * 方式一:Spring 管理的单例(推荐).
 * 通过 @Component/@Service 注解,Spring 容器保证单例.
 */
@Component
public class GlobalConfig {
    private Map<String, String> configMap = new ConcurrentHashMap<>();

    public String get(String key) {
        return configMap.get(key);
    }

    public void set(String key, String value) {
        configMap.put(key, value);
    }
}

/**
 * 方式二:双重检查锁(非 Spring 环境).
 */
public class ConnectionPool {
    private static volatile ConnectionPool instance;

    private ConnectionPool() {
        // 初始化连接池
    }

    public static ConnectionPool getInstance() {
        if (instance == null) {
            synchronized (ConnectionPool.class) {
                if (instance == null) {
                    instance = new ConnectionPool();
                }
            }
        }
        return instance;
    }
}
相关推荐
货拉拉技术1 小时前
复杂业务规则太多?我们搭了一个规则可视化配置平台
后端
爱勇宝2 小时前
AI 都能写代码了,还要学计算机吗?
前端·后端·程序员
cfm_29142 小时前
Spring监听器
java·spring boot·后端
逝水无殇2 小时前
C# 特性详解
开发语言·后端·c#
cfm_29142 小时前
SpringBoot 数据库全栈整合
数据库·spring boot·后端
xn71332 小时前
别再把 MCP Roots 当安全沙箱:一个符号链接就能逃逸目录
人工智能·后端·mcp
SamDeepThinking2 小时前
Java面向对象在JVM里怎么实现
java·后端·面试
学以智用2 小时前
HTTP 请求头 Headers 完整详解
前端·后端
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(三十五):控制台静态页面、深入研究时间格式化方案
后端·go·ai编程