设计模式与架构模式(高级)——面向对象与函数式结合的实战指南!
开篇语
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
1. 常用设计模式重审(何时用 / 如何实现 / 函数式变体)
工厂(Factory)
-
意图:把对象创建与使用分离,隐藏具体实现,便于扩展、测试与替换。
-
何时用:当创建逻辑复杂、需根据上下文/配置/策略返回不同实现时。
-
实现要点:
- 简单工厂:静态方法根据输入返回不同实现。
- 抽象工厂/工厂方法:将创建责任推给子类或外部配置。
- 在 DI 框架下:用
@Configuration/@Bean或Provider/FactoryBean实现可插拔创建。
-
函数式变体:工厂返回
Supplier<T>或Function<Context, T>,工厂由高阶函数构建,便于组合与测试。
策略(Strategy)
-
意图:把算法/行为封装成可替换的策略,实现运行时替换或组合。
-
何时用:有大量
if/else根据类型/配置切换行为时。 -
实现要点:
- 定义策略接口
Policy,把实现注册到Map<PolicyKey, Policy>或用 DI 的@Qualifier注入集合。 - 在工厂/上下文中选择策略并执行。
- 定义策略接口
-
函数式变体:策略实现为
Function<Request, Result>,用高阶函数/组合器组合多个策略(例如andThen/compose)。
示例(策略替换 if-else,Java)
public interface PricingStrategy {
Money apply(Order order);
}
@Component
public class DefaultPricingStrategy implements PricingStrategy { ... }
@Component("vip")
public class VipPricingStrategy implements PricingStrategy { ... }
@Service
public class PricingService {
private final Map<String, PricingStrategy> strategies;
public PricingService(List<PricingStrategy> list) {
this.strategies = list.stream().collect(toMap(s -> s.getClass().getSimpleName().toLowerCase(), s -> s));
}
public Money price(Order o){
return strategies.getOrDefault(o.getCustomerType(), strategies.get("default")).apply(o);
}
}
责任链(Chain of Responsibility)
- 意图:把处理请求的不同处理器串成链,按顺序尝试处理,减少条件分支。
- 场景:复杂验证、拦截器、过滤器、审批流。
- 实现要点:每个 handler 负责
bool handle(ctx),不处理则传递给下一个。可用函数式流水线Stream/Flux实现链式处理。
事件驱动(Event-Driven)
- 意图:松耦合组件通过事件异步通信,解耦执行顺序并提高可扩展性。
- 场景:跨模块通知、异步补偿、扩展点。
- 实现要点:定义事件类型、发布者、订阅者(同步/异步),考虑可靠投递(至少一次/恰好一次)、幂等性与事件溯源(Event Sourcing)。
- 函数式变体:事件流为
Flux<Event>,用操作符做流式处理与组合。
CQRS(Command Query Responsibility Segregation)
-
意图:把写模型(Command)与读模型(Query)分离,优化可伸缩性与一致性模型。
-
何时用:读写模式、读取高并发但写少、需要不同存储/表示的场景。
-
实现要点:
- 写侧:事件或命令处理,更新权威源(Event Store/DB)。
- 读侧:投影/物化视图,通常为专门的查询 DB(优化查询)。
- 关注:最终一致性、投影延迟、事务边界、回滚与补偿。
-
与 Event Sourcing 结合:写侧把状态变更记录为事件流,读侧由事件构建投影。
Event Sourcing(事件溯源)
- 意图:不存状态快照(或次要快照),而存所有使状态变更的事件,通过重放事件重建实体状态。
- 优点:完全审计、易于回放/调试、兼容 CQRS。
- 缺点:实现复杂、事件版本化、存储成本、快照策略与合并策略要设计好。
- 实现要点:事件版本控制、快照机制、幂等消费、事件合规(schema evolutions)。
2. 微服务 / 分布式下的架构模式(实战重点)
(短列:核心思想、常见实现、工程注意点)
Saga(分布式事务的补偿方案)
-
两种风格:
- Choreography(编排式):服务通过事件通知彼此,链式触发流转(无中心)。优点:松耦合;缺点:难以观测/验证全流程状态。
- Orchestration(协调器):单一 Saga 协调器负责执行命令并在失败时触发补偿。优点:易于跟踪与控制;缺点:有中心依赖。
-
实现要点:
- 每步实现本地事务并发布事件或等待协调器命令。
- 失败时提供补偿操作(Compensating Action),补偿可能是逆操作或补偿事件。
- 设计 idempotency(幂等)和保证顺序。
- 建议用状态机保存 Saga 状态(pending → success/failure → compensating → done)。
-
工程实践:用消息中间件(Kafka/Rabbit)、持久化 Saga 状态、超时/重试策略、可视化监控。
Circuit Breaker(熔断器)
- 目的:当被依赖服务出现故障或高延迟时短路调用,保护自身资源。
- 实现要点:统计失败率/延迟、阈值打开熔断、定时半开探测,结合重试与退避(exponential backoff)。
- 常用实现:Resilience4j、Hystrix(已退役)等。注意:熔断器应与限流、bulkhead 共同使用。
Bulkhead(舱壁)
- 目的:隔离资源(线程池、连接池、队列)防止单一故障耗尽整个系统资源。
- 实现要点:按功能/上下文分配独立线程池或限制并发请求数;在容器层用 Kubernetes 的资源限额实现隔离。
- 与其他模式搭配:结合 Circuit Breaker、限流(Rate Limiting)和负载剪裁(Load Shedding)。
其他:Retry、Timeout、Rate Limiting、Back-pressure
- 最佳实践:短路优先(timeout → fallback → retry with backoff),确保幂等;在链路中限制最大并发和请求队列长度。
3. 模块化、插件化设计实践(工程级实践)
-
分层与边界:保持清晰的模块边界(API/DTO/Domain),用依赖反转(接口)隐藏实现细节。
-
插件化实现策略:
- Java ServiceLoader:适合简单插件加载。
- SPI + ClassLoader:更灵活的热插拔(但要小心类加载器隔离)。
- OSGi / Module System:需要更强的模块化支持与生命周期管理。
- 在 Spring 中:把 plugin 实现作为模块,使用
@ConditionalOnProperty/@Profile激活;或使用SpringFactoriesLoader自动发现。
-
接口设计原则(为了插件化):
- 保持稳定的接口契约(尽量向后兼容)。
- 使用 adapter/facade 隔离内部变化。
- 明确插件生命周期(init/start/stop)与资源清理。
-
测试与安全:对插件隔离运行时进行限制;对第三方插件做签名与权限校验(安全沙箱)。
4. 设计模式的反模式与可维护性考量
-
过度设计(Overengineering):把“模式”当作模板机械复制,导致代码复杂且难懂。原则:简单优先,先实现直观版本,必要时再抽象。
-
金锤(Golden Hammer):每个问题都用熟悉的模式(例如用事件驱动解决同步问题)会导致成本膨胀。
-
上帝对象 / 巨型服务:没有明确边界,造成难以演化与测试。
-
过度抽象:过多的接口和抽象层会增加认知成本。衡量标准:是否提高可测试性、是否减少重复、是否提升可替换性。
-
忘记可观察性:分布式系统没有充足的 tracing/metrics/logs,会把模式变成黑箱。
-
可维护性 checklist:
- 代码能被读懂(少量注释 + 明确命名)
- 单元测试覆盖策略边界
- 集成测试覆盖失败/补偿路径
- 监控与告警(SLO、p95/p99、错误率)
- 设计文档(接口契约/事件规范)
5. 实战练习(两项:策略替换 if-else;实现简单 Saga 流程)
练习 A:用策略模式替换 if-else 污染点(步骤)
-
识别:找到大量基于
type/enum的switch/if代码块(例如付款方式:if (type==ALIPAY) ... else if (WECHAT) ...)。 -
抽象接口:
public interface PaymentHandler { boolean supports(PaymentType t); PaymentResult handle(PaymentRequest req); } -
实现类:每个支付方式作单独实现,并用注解/配置注册到容器。
-
策略选择器(Registry):
public class PaymentRouter { private final List<PaymentHandler> handlers; public PaymentResult route(req){ return handlers.stream() .filter(h -> h.supports(req.getType())) .findFirst() .orElseThrow(...) .handle(req); } } -
测试与回归:用单元测试覆盖每个 handler;删除旧的 if/else;逐步回退保障。
练习 B:实现一个简单 Saga(Order→Payment→Inventory)
场景
- 创建订单(Order Service)需完成支付(Payment Service)并扣库存(Inventory Service)。若任一步失败,需回滚已完成步骤(补偿)。
Orchestrator(协调型)示例思路(简化)
-
Saga 状态机:
NEW -> PAYMENT_PENDING -> INVENTORY_PENDING -> COMPLETED,失败走COMPENSATING -> ROLLBACK_DONE。 -
持久化 Saga:将当前步骤、相关数据、重试计数持久化(数据库表
sagas)。 -
步骤实现:
- Orchestrator 发起
Payment请求(同步或异步),记录PAYMENT_PENDING。 - 若 Payment 成功,调用 Inventory 扣减并记录
INVENTORY_PENDING。 - 若 Inventory 成功,标记 Saga
COMPLETED。 - 若任何一步失败,按已完成的步骤按逆序执行补偿(例如:若 Inventory 失败且 Payment 成功,则调用 Payment Refund)。
- Orchestrator 发起
-
幂等与重试:
- 每个服务的操作(charge、reserve、refund)都应实现幂等(通过 requestId 或 operationId)。
- Orchestrator 对失败使用指数退避重试,超过阈值进入人工补救或告警。
-
事件驱动替代(Choreography):
- 服务发布事件(
OrderCreated→ Payment listens → PaymentSuccess publishesPaymentSucceeded→ Inventory listens …),失败由相应服务发布PaymentFailed`,但需要机制保证补偿。
- 服务发布事件(
-
示例伪代码(Orchestrator):
public class OrderSagaOrchestrator {
void startSaga(OrderDto order){
Saga s = sagaRepo.create(order);
try {
paymentService.charge(order);
s.setState(PAYMENT_DONE); sagaRepo.save(s);
inventoryService.reserve(order);
s.setState(INVENTORY_DONE); sagaRepo.save(s);
s.setState(COMPLETED); sagaRepo.save(s);
} catch (Exception e) {
compensate(s); // reverse completed steps
}
}
void compensate(Saga s){
if (s.state >= PAYMENT_DONE) paymentService.refund(s.order);
// additional compensations...
s.setState(ROLLED_BACK); sagaRepo.save(s);
}
}
- 监控/可观测性:记录 Saga 执行时间、失败率、补偿频次,集成分布式 trace(OpenTelemetry)与日志。
6. 常见陷阱(再提醒)
- 把模式当成银弹:先验证痛点再引入复杂模式(例如 CQRS/EventSourcing 不应作为默认实现)。
- 忽视一致性模型:分布式系统必须明确是强一致性还是最终一致性,设计要反映 SLA/业务要求。
- 未处理幂等:分布式重试会导致重复执行,所有关键操作必须保证幂等或可补偿。
- 缺少测试与监控:设计再优雅,缺乏集成测试与监控也等于“盲人修车”。
- 滥用消息(事件泛滥):事件粒度设计不当会引发复杂的依赖链与调试难度。
7. 推荐练习路径 & 验收标准
-
小步实验:
- 在单体应用里把一个
if/else分支替换为策略模式并编写完整单元测试(含边界错误)。 - 用两三个微服务实现一个简单的 Saga(可用本地 Kafka 或 Rabbit),包含持久化 Saga 状态、补偿逻辑、并写集成测试。
- 在单体应用里把一个
-
验收标准:
- 代码可读且每个模式有测试覆盖;
- 异常路径(失败、超时)被测试且能触发补偿/回滚;
- 有度量与追踪(比如 p95 latency、Saga failure rate、补偿率)。
8. 延伸阅读(经典 & 实用)
- 《设计模式:可复用面向对象软件的基础》(GoF)——模式语义与意图。
- 《Patterns of Enterprise Application Architecture》——企业级架构模式。
- 《Microservices Patterns》(Chris Richardson)——Saga、Transaction、Event-driven patterns。
- 《Designing Data-Intensive Applications》(Martin Kleppmann)——事件源、日志、复制与一致性。
- 关注 Resilience4j、OpenTelemetry、Event Sourcing / CQRS 实例库与社区文章。
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!
- 点赞
- 收藏
- 关注作者
评论(0)