【Java 23种设计模式实战】
Java 23种设计模式实战:从电商订单系统出发,告别Hello World
写在前面:网上设计模式教程满天飞,但大多数都是
Animal → Dog → Cat的玩具示例。本文用一套完整的电商/订单/支付/用户系统贯穿全部 23 种 GoF 模式,每个模式都有可运行的main()方法,控制台输出真实业务结果。全部代码已编译通过,文末附完整项目地址。
目录
- 一、为什么要学设计模式?
- 二、23种模式总览
- 三、创建型模式(5种)—— 对象怎么创建
- 四、行为型模式(11种)—— 对象怎么协作
- 五、结构型模式(7种)—— 对象怎么组合
- 六、易混淆模式对比
- 七、设计原则对照
- 八、Spring框架中的设计模式
- 九、学习路径建议
一、为什么要学设计模式?
先看一段没学设计模式时可能会写的代码:
// 典型的"屎山"代码
public String payOrder(String orderId, String payMethod, String discountType) {
// 支付逻辑:一堆 if-else
if ("wechat".equals(payMethod)) {
// 微信支付逻辑...
} else if ("alipay".equals(payMethod)) {
// 支付宝逻辑...
} else if ("bank".equals(payMethod)) {
// 银行卡逻辑...
}
// 折扣逻辑:又一堆 if-else
if ("fullReduction".equals(discountType)) {
// 满减...
} else if ("coupon".equals(discountType)) {
// 优惠券...
} else if ("member".equals(discountType)) {
// 会员折扣...
}
// 还混着权限校验、日志记录、缓存逻辑...
return "success";
}
问题在哪?
- 每加一种支付方式,就要改这个方法 → 违反开闭原则
- 支付、折扣、权限、日志全混在一个方法里 → 违反单一职责
- 想测试某种折扣逻辑,必须连支付逻辑一起跑 → 难以测试
- 代码越来越长,没人敢动 → 技术债雪球
设计模式解决的就是这些问题。 不是为了装X,而是为了让代码:能扩展、好维护、易测试。
二、23种模式总览
| 分类 | 模式 | 业务场景 | 一句话理解 |
|---|---|---|---|
| 创建型 | 单例 Singleton | 数据库连接池 | 全局只有一个实例 |
| 工厂方法 Factory Method | 日志记录器 | 父类定接口,子类定实现 | |
| 抽象工厂 Abstract Factory | 云服务商资源 | 创建一组相关对象 | |
| 建造者 Builder | 订单构建 | 分步构建复杂对象 | |
| 原型 Prototype | 促销模板 | clone() 快速复制 | |
| 行为型 | 策略 Strategy | 折扣/支付 | 算法可互换,消除if-else |
| 责任链 Chain | 审批流程 | 链式传递,各管一段 | |
| 观察者 Observer | 事件通知 | 发布-订阅,松耦合 | |
| 状态 State | 订单状态机 | 状态驱动行为 | |
| 命令 Command | 订单操作 | 请求封装为对象,可撤销 | |
| 模板方法 Template | 订单流程 | 骨架固定,步骤可重写 | |
| 解释器 Interpreter | SQL条件解析 | 字符串→AST→求值 | |
| 访问者 Visitor | 多格式导出 | 双分派,操作与数据分离 | |
| 备忘录 Memento | 状态快照 | 保存/恢复对象状态 | |
| 迭代器 Iterator | 分页遍历 | 统一遍历接口 | |
| 中介者 Mediator | 模块协调 | 多对多→多对一 | |
| 结构型 | 代理 Proxy | 权限/缓存/日志 | 控制访问,AOP底层 |
| 装饰器 Decorator | 价格叠加 | 包装增强,不改原类 | |
| 适配器 Adapter | 支付SDK适配 | 转换接口 | |
| 外观 Facade | 支付入口 | 简化子系统调用 | |
| 桥接 Bridge | 消息发送 | 两个维度独立变化 | |
| 组合 Composite | 组织架构树 | 统一处理树形结构 | |
| 享元 Flyweight | 商品池 | 共享对象省内存 |
三、创建型模式(5种)—— 对象怎么创建
3.1 单例模式:数据库连接池
场景:数据库连接池必须全局唯一,不能每个模块各建一个。
5种实现方式对比:
| 实现 | 线程安全 | 延迟加载 | 性能 | 防反射 | 推荐度 |
|---|---|---|---|---|---|
| 饿汉式 | ✅ | ❌ | 高 | ❌ | ⭐⭐ |
| 懒汉(同步方法) | ✅ | ✅ | 差 | ❌ | ⭐ |
| 双重检查锁(DCL) | ✅ | ✅ | 高 | ❌ | ⭐⭐⭐⭐ |
| 静态内部类 | ✅ | ✅ | 高 | ❌ | ⭐⭐⭐⭐⭐ |
| 枚举 | ✅ | ❌ | 高 | ✅ | ⭐⭐⭐⭐ |
DCL 实现(最常用):
class DatabaseConnectionPool {
// volatile 防止指令重排
private static volatile DatabaseConnectionPool instance;
private String url;
private int maxPoolSize;
private int activeConnections = 0;
private DatabaseConnectionPool() {}
public static DatabaseConnectionPool getInstance() {
if (instance == null) { // 第一次检查:避免不必要的加锁
synchronized (DatabaseConnectionPool.class) {
if (instance == null) { // 第二次检查:防止重复创建
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}
public Connection getConnection() {
if (activeConnections >= maxPoolSize) {
throw new RuntimeException("连接池已满");
}
activeConnections++;
return new Connection(url, activeConnections);
}
}
为什么要 volatile?
instance = new DatabaseConnectionPool()不是原子操作,分3步:1)分配内存 2)初始化对象 3)引用指向内存。如果不加 volatile,JVM 可能重排为 1→3→2,其他线程在步骤3之后、步骤2之前拿到一个未初始化的实例。
静态内部类实现(最优雅,推荐):
class ConnectionPoolHolder {
private ConnectionPoolHolder() {}
// 利用类加载机制保证线程安全,且延迟加载
private static class Holder {
private static final ConnectionPoolHolder INSTANCE = new ConnectionPoolHolder();
}
public static ConnectionPoolHolder getInstance() {
return Holder.INSTANCE; // 只有第一次调用时才触发 Holder 类加载
}
}
3.2 工厂方法:日志记录器
场景:开发环境用控制台日志,生产环境用文件日志,测试环境用内存日志。
// 产品接口
interface Logger {
void log(String message);
void error(String message);
}
// 具体产品
class ConsoleLogger implements Logger {
public void log(String msg) { System.out.println("[CONSOLE] " + msg); }
public void error(String msg) { System.err.println("[CONSOLE-ERROR] " + msg); }
}
class FileLogger implements Logger {
public void log(String msg) { /* 写入文件 */ }
public void error(String msg) { /* 写入文件 */ }
}
// 工厂接口
interface LoggerFactory {
Logger createLogger();
}
// 具体工厂
class ConsoleLoggerFactory implements LoggerFactory {
public Logger createLogger() { return new ConsoleLogger(); }
}
class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() { return new FileLogger(); }
}
核心思想:调用方不关心 new ConsoleLogger() 还是 new FileLogger(),只跟工厂接口打交道。
与简单工厂的区别:简单工厂是一个工厂类里用 switch-case 决定创建哪个;工厂方法是为每种产品各建一个工厂类,符合开闭原则。
3.3 抽象工厂:云服务商资源族
场景:系统要支持腾讯云和阿里云,每家云的云服务器、对象存储、数据库都不一样,但都是一组配套使用的。
// 抽象产品族
interface CloudServer { void start(); void stop(); }
interface ObjectStorage { void upload(String key, byte[] data); }
interface CloudDatabase { void connect(); void execute(String sql); }
// 抽象工厂
interface CloudFactory {
CloudServer createServer();
ObjectStorage createStorage();
CloudDatabase createDatabase();
}
// 腾讯云工厂
class TencentCloudFactory implements CloudFactory {
public CloudServer createServer() { return new CVM(); }
public ObjectStorage createStorage() { return new COS(); }
public CloudDatabase createDatabase() { return new TDSQL(); }
}
// 阿里云工厂
class AliyunFactory implements CloudFactory {
public CloudServer createServer() { return new ECS(); }
public ObjectStorage createStorage() { return new OSS(); }
public CloudDatabase createDatabase() { return new RDS(); }
}
工厂方法 vs 抽象工厂:工厂方法创建一个产品;抽象工厂创建一族产品。如果你需要一组配套使用的对象(选了腾讯云的服务器就得配腾讯云的存储),用抽象工厂。
3.4 建造者模式:订单构建
场景:一个订单有十几个字段,有些必填有些选填,构造函数参数爆炸。
// 链式调用构建
Order order = new Order.Builder("ORD-001", "张三")
.product("MacBook Pro 14")
.quantity(1)
.unitPrice(14999.00)
.shippingAddress("西安市雁塔区...")
.paymentMethod("alipay")
.couponCode("SUMMER100")
.remark("工作日送达")
.build();
实现要点:
class Order {
private final String orderId; // 必填
private final String customer; // 必填
private String product; // 选填
private int quantity; // 选填
private double unitPrice; // 选填
// ... 其他选填字段
private Order(Builder builder) {
this.orderId = builder.orderId;
this.customer = builder.customer;
this.product = builder.product;
// ...
}
public static class Builder {
private final String orderId;
private final String customer;
public Builder(String orderId, String customer) {
this.orderId = orderId;
this.customer = customer;
}
public Builder product(String product) { this.product = product; return this; }
public Builder quantity(int quantity) { this.quantity = quantity; return this; }
public Builder unitPrice(double price) { this.unitPrice = price; return this; }
public Order build() {
return new Order(this);
}
}
}
Lombok 的
@Builder注解就是干这个的,底层自动生成上面的代码。
3.5 原型模式:促销模板复制
场景:运营创建了一个"双11满减促销"模板,之后要基于它快速创建几十个类似的促销活动,一个个 new 太慢。
class PromotionTemplate implements Cloneable {
String name;
String type; // FULL_REDUCTION / COUPON / DISCOUNT
double threshold;
double discount;
List<String> applicableProducts;
@Override
public PromotionTemplate clone() {
try {
PromotionTemplate cloned = (PromotionTemplate) super.clone();
// 深拷贝:列表也要复制
cloned.applicableProducts = new ArrayList<>(this.applicableProducts);
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
使用:
PromotionTemplate template = new PromotionTemplate();
template.name = "双11满减";
template.type = "FULL_REDUCTION";
template.threshold = 1000;
template.discount = 100;
template.applicableProducts = Arrays.asList("电子产品", "家居");
// 基于模板快速创建
PromotionTemplate electronics = template.clone();
electronics.name = "电子产品专场满减";
electronics.applicableProducts.add("数码配件");
PromotionTemplate homeAppliances = template.clone();
homeAppliances.name = "家居专场满减";
homeAppliances.threshold = 500;
深拷贝 vs 浅拷贝:
super.clone()是浅拷贝,引用类型字段会共享同一个对象。如果模板里有个List,两个克隆体改的是同一个 List。所以必须手动深拷贝引用类型字段。
四、行为型模式(11种)—— 对象怎么协作
4.1 策略模式:折扣与支付
场景:订单系统有多种折扣方式(不打折/满减/优惠券/会员折)和多种支付方式(微信/支付宝/银行卡),运行时动态切换。
不用策略模式:
// 每加一种折扣就要改这个方法
double calculatePrice(double original, String discountType) {
if ("none".equals(discountType)) return original;
if ("fullReduction".equals(discountType)) return original >= 1000 ? original - 100 : original;
if ("coupon".equals(discountType)) return original - 50;
if ("member".equals(discountType)) return original * 0.8;
return original;
}
用策略模式:
// 策略接口
interface DiscountStrategy {
double calculate(double originalPrice);
}
// 四种策略实现
class NoDiscountStrategy implements DiscountStrategy {
public double calculate(double price) { return price; }
}
class FullReductionStrategy implements DiscountStrategy {
private double threshold, reduction;
FullReductionStrategy(double threshold, double reduction) {
this.threshold = threshold; this.reduction = reduction;
}
public double calculate(double price) {
return price >= threshold ? price - reduction : price;
}
}
class CouponStrategy implements DiscountStrategy {
private double couponValue;
CouponStrategy(double couponValue) { this.couponValue = couponValue; }
public double calculate(double price) { return Math.max(0, price - couponValue); }
}
class MemberDiscountStrategy implements DiscountStrategy {
private double rate;
MemberDiscountStrategy(double rate) { this.rate = rate; }
public double calculate(double price) { return price * rate; }
}
运行时切换:
double originalPrice = 1000.00;
// 策略1:不打折
DiscountStrategy s1 = new NoDiscountStrategy();
System.out.println("不打折: ¥" + s1.calculate(originalPrice)); // 1000.0
// 策略2:满1000减100
DiscountStrategy s2 = new FullReductionStrategy(1000, 100);
System.out.println("满减: ¥" + s2.calculate(originalPrice)); // 900.0
// 策略3:会员8折
DiscountStrategy s3 = new MemberDiscountStrategy(0.8);
System.out.println("会员折: ¥" + s3.calculate(originalPrice)); // 800.0
新增折扣方式时:只需要新增一个实现类,不改任何已有代码。这就是开闭原则。
4.2 责任链模式:订单审批流
场景:退款审批,金额不同走不同审批人:<1000 客服批 → <10000 经理批 → <100000 总监批 → ≥100000 CEO批。
// 抽象审批者
abstract class Approver {
protected Approver next; // 下一个审批者
protected String name;
protected double maxAmount; // 审批权限上限
void setNext(Approver next) { this.next = next; }
void approve(RefundRequest req) {
if (req.amount <= maxAmount) {
doApprove(req); // 权限内,直接批
} else if (next != null) {
System.out.println(name + ": 超出权限,转交上级");
next.approve(req); // 传给下一个
} else {
System.out.println(name + ": 无人可审批此金额");
}
}
abstract void doApprove(RefundRequest req);
}
// 具体审批者
class CustomerServiceApprover extends Approver {
CustomerServiceApprover() { this.name = "客服"; this.maxAmount = 1000; }
void doApprove(RefundRequest req) {
System.out.println("客服: 批准退款 " + req.requestId + " ¥" + req.amount);
}
}
class ManagerApprover extends Approver {
ManagerApprover() { this.name = "部门经理"; this.maxAmount = 10000; }
void doApprove(RefundRequest req) {
System.out.println("经理: 批准退款 " + req.requestId + " ¥" + req.amount);
}
}
class DirectorApprover extends Approver {
DirectorApprover() { this.name = "总监"; this.maxAmount = 100000; }
void doApprove(RefundRequest req) {
System.out.println("总监: 批准退款 " + req.requestId + " ¥" + req.amount);
}
}
class CeoApprover extends Approver {
CeoApprover() { this.name = "CEO"; this.maxAmount = Double.MAX_VALUE; }
void doApprove(RefundRequest req) {
System.out.println("CEO: 批准退款 " + req.requestId + " ¥" + req.amount);
}
}
构建审批链:
// 组装责任链
Approver customerService = new CustomerServiceApprover();
Approver manager = new ManagerApprover();
Approver director = new DirectorApprover();
Approver ceo = new CeoApprover();
customerService.setNext(manager);
manager.setNext(director);
director.setNext(ceo);
// 测试不同金额
customerService.approve(new RefundRequest("R001", 500, "商品质量问题")); // 客服批
customerService.approve(new RefundRequest("R002", 5000, "发错货")); // 经理批
customerService.approve(new RefundRequest("R003", 50000, "大批量退货")); // 总监批
customerService.approve(new RefundRequest("R004", 200000,"企业客户退款")); // CEO批
运行输出:
客服: 批准退款 R001 ¥500.0
---
客服: 超出权限,转交上级
部门经理: 批准退款 R002 ¥5000.0
---
客服: 超出权限,转交上级
部门经理: 超出权限,转交上级
总监: 批准退款 R003 ¥50000.0
---
客服: 超出权限,转交上级
部门经理: 超出权限,转交上级
总监: 超出权限,转交上级
CEO: 批准退款 R004 ¥200000.0
Spring 中的责任链:
HandlerInterceptorChain就是责任链模式,每个拦截器决定放行还是拦截。
4.3 观察者模式:订单事件通知
场景:用户下单后,需要同时触发:发短信通知、发邮件确认、扣减库存、推送数据给BI系统。这些操作互相独立,且未来可能增加新的通知方式。
// 事件
class OrderEvent {
String orderId;
String userId;
double amount;
// ...
}
// 观察者接口
interface OrderEventListener {
void onOrderCreated(OrderEvent event);
}
// 多个监听器
class SmsNotifier implements OrderEventListener {
public void onOrderCreated(OrderEvent event) {
System.out.println("[短信] 通知用户 " + event.userId + " 订单 " + event.orderId + " 已创建");
}
}
class EmailNotifier implements OrderEventListener {
public void onOrderCreated(OrderEvent event) {
System.out.println("[邮件] 发送订单确认邮件: " + event.orderId);
}
}
class InventoryDeductor implements OrderEventListener {
public void onOrderCreated(OrderEvent event) {
System.out.println("[库存] 扣减订单 " + event.orderId + " 对应商品库存");
}
}
// 被观察者(事件发布者)
class OrderService {
private List<OrderEventListener> listeners = new ArrayList<>();
void addListener(OrderEventListener listener) { listeners.add(listener); }
void createOrder(OrderEvent event) {
System.out.println("订单创建: " + event.orderId);
// 通知所有监听器
for (OrderEventListener listener : listeners) {
listener.onOrderCreated(event);
}
}
}
使用:
OrderService orderService = new OrderService();
orderService.addListener(new SmsNotifier());
orderService.addListener(new EmailNotifier());
orderService.addListener(new InventoryDeductor());
orderService.createOrder(new OrderEvent("ORD-001", "U10086", 1299.00));
新增通知方式:只需新增一个
XxxNotifier implements OrderEventListener,注册进去就行,完全不改OrderService。
4.4 状态模式:订单状态机
场景:订单有多个状态(待支付→已支付→已发货→已完成/已取消),不同状态下能做的操作不同。
不用状态模式(满屏if-else):
void pay(Order order) {
if (order.status.equals("PENDING")) {
order.status = "PAID";
} else if (order.status.equals("PAID")) {
throw new RuntimeException("已支付,不能重复支付");
} else if (order.status.equals("SHIPPED")) {
throw new RuntimeException("已发货,不能支付");
}
// ... 每个操作都要判断所有状态
}
用状态模式:
// 状态接口
interface OrderState {
void pay(OrderContext order);
void ship(OrderContext order);
void deliver(OrderContext order);
void cancel(OrderContext order);
String getStateName();
}
// 待支付状态
class PendingState implements OrderState {
public void pay(OrderContext order) {
System.out.println("支付成功");
order.setState(new PaidState());
}
public void ship(OrderContext order) { System.out.println("待支付状态不可发货"); }
public void deliver(OrderContext order) { System.out.println("待支付状态不可确认收货"); }
public void cancel(OrderContext order) {
System.out.println("订单已取消");
order.setState(new CancelledState());
}
public String getStateName() { return "待支付"; }
}
// 已支付状态
class PaidState implements OrderState {
public void pay(OrderContext order) { System.out.println("已支付,请勿重复支付"); }
public void ship(OrderContext order) {
System.out.println("已发货");
order.setState(new ShippedState());
}
public void deliver(OrderContext order) { System.out.println("已支付但未发货,不可确认收货"); }
public void cancel(OrderContext order) {
System.out.println("已支付订单取消,发起退款");
order.setState(new CancelledState());
}
public String getStateName() { return "已支付"; }
}
// ... ShippedState, DeliveredState, CancelledState 类似
// 上下文
class OrderContext {
private OrderState state;
OrderContext() { this.state = new PendingState(); }
void setState(OrderState state) {
this.state = state;
System.out.println("状态变更为: " + state.getStateName());
}
void pay() { state.pay(this); }
void ship() { state.ship(this); }
void deliver() { state.deliver(this); }
void cancel() { state.cancel(this); }
}
核心区别:状态模式中,状态自己决定下一步跳到哪个状态。策略模式中,外部决定用哪个策略。两者结构几乎一样,但语义不同。
4.5 装饰器模式:订单价格叠加
场景:订单最终价格 = 商品原价 → 满减 → 优惠券 → 会员折扣 → 积分抵扣。每一层优惠都是"叠加"在前一层之上。
// 核心接口
interface PriceCalculator {
double calculate();
}
// 基础实现:原价
class BasePrice implements PriceCalculator {
private double price;
BasePrice(double price) { this.price = price; }
public double calculate() { return price; }
}
// 装饰器基类(持有被装饰对象的引用)
abstract class PriceDecorator implements PriceCalculator {
protected PriceCalculator wrapped;
PriceDecorator(PriceCalculator wrapped) { this.wrapped = wrapped; }
}
// 满减装饰器
class FullReductionDecorator extends PriceDecorator {
private double threshold, reduction;
FullReductionDecorator(PriceCalculator wrapped, double threshold, double reduction) {
super(wrapped);
this.threshold = threshold;
this.reduction = reduction;
}
public double calculate() {
double price = wrapped.calculate(); // 先算前一层的价格
return price >= threshold ? price - reduction : price;
}
}
// 优惠券装饰器
class CouponDecorator extends PriceDecorator {
private double couponValue;
CouponDecorator(PriceCalculator wrapped, double couponValue) {
super(wrapped);
this.couponValue = couponValue;
}
public double calculate() {
return Math.max(0, wrapped.calculate() - couponValue);
}
}
// 会员折扣装饰器
class MemberDiscountDecorator extends PriceDecorator {
private double rate;
MemberDiscountDecorator(PriceCalculator wrapped, double rate) {
super(wrapped);
this.rate = rate;
}
public double calculate() {
return wrapped.calculate() * rate;
}
}
自由组合叠加:
double basePrice = 1000.00;
// 方案1:只有原价 → 1000
PriceCalculator p1 = new BasePrice(basePrice);
// 方案2:原价 + 满减(满1000减100) → 900
PriceCalculator p2 = new FullReductionDecorator(
new BasePrice(basePrice), 1000, 100);
// 方案3:原价 + 满减 + 优惠券(-50) → 850
PriceCalculator p3 = new CouponDecorator(
new FullReductionDecorator(
new BasePrice(basePrice), 1000, 100), 50);
// 方案4:原价 + 满减 + 优惠券 + 会员9折 → 765
PriceCalculator p4 = new MemberDiscountDecorator(
new CouponDecorator(
new FullReductionDecorator(
new BasePrice(basePrice), 1000, 100), 50), 0.9);
装饰器 vs 代理:结构几乎一样(都持有目标对象引用),但意图不同——装饰器是"增强功能",代理是"控制访问"。
4.6 代理模式:用户服务权限控制
场景:用户服务接口需要权限校验 + 访问日志 + 结果缓存,但不想把这些横切逻辑写进 UserServiceImpl。
// 接口
interface UserService {
String getUser(String userId);
void deleteUser(String userId);
}
// 真实实现(只关心业务)
class UserServiceImpl implements UserService {
public String getUser(String userId) {
try { Thread.sleep(50); } catch (Exception e) {} // 模拟DB查询
System.out.println("[DB] 查询用户 " + userId);
return "用户信息: {id=" + userId + ", name=张三}";
}
public void deleteUser(String userId) {
System.out.println("[DB] 删除用户 " + userId);
}
}
// 代理类:权限 + 日志 + 缓存
class UserServiceProxy implements UserService {
private UserService target;
private String role;
private Map<String, String> cache = new HashMap<>();
UserServiceProxy(UserService target, String role) {
this.target = target;
this.role = role;
}
public String getUser(String userId) {
long start = System.currentTimeMillis();
// 1. 缓存检查
if (cache.containsKey(userId)) {
System.out.println("[代理] 命中缓存");
return cache.get(userId);
}
// 2. 调用真实方法
String result = target.getUser(userId);
// 3. 写入缓存
cache.put(userId, result);
// 4. 记录日志
System.out.println("[代理] 耗时=" + (System.currentTimeMillis() - start) + "ms");
return result;
}
public void deleteUser(String userId) {
// 权限校验
if (!"ADMIN".equals(role)) {
System.out.println("[代理] 权限不足: " + role);
return;
}
System.out.println("[代理] 访问日志: deleteUser(" + userId + ")");
target.deleteUser(userId);
cache.remove(userId); // 清缓存
}
}
Spring AOP 底层就是这个。
@Transactional、@Cacheable、@Async等注解,都是通过动态代理在方法前后织入横切逻辑。
4.7 解释器模式:SQL条件解析器
场景:运营后台需要根据用户输入的条件表达式动态过滤订单,如 status = 'PAID' AND amount > 1000 OR region = 'EAST'。
语法规则(BNF):
expr → term ('OR' term)*
term → factor ('AND' factor)*
factor → 'key op value' | '(' expr ')'
op → '=' | '!=' | '>' | '<' | '>=' | '<='
抽象表达式:
interface Expression {
boolean interpret(Map<String, Object> context);
}
// 终结符表达式:key op value
class ComparisonExpression implements Expression {
private final String key;
private final String operator;
private final Object value;
public boolean interpret(Map<String, Object> context) {
Object actual = context.get(key);
if (actual == null) return false;
switch (operator) {
case "=": return actual.toString().equals(value.toString());
case "!=": return !actual.toString().equals(value.toString());
case ">": return toDouble(actual) > toDouble(value);
case "<": return toDouble(actual) < toDouble(value);
case ">=": return toDouble(actual) >= toDouble(value);
case "<=": return toDouble(actual) <= toDouble(value);
default: return false;
}
}
// ...
}
// 非终结符:AND
class AndExpression implements Expression {
private final Expression left, right;
public boolean interpret(Map<String, Object> ctx) {
return left.interpret(ctx) && right.interpret(ctx);
}
}
// 非终结符:OR
class OrExpression implements Expression {
private final Expression left, right;
public boolean interpret(Map<String, Object> ctx) {
return left.interpret(ctx) || right.interpret(ctx);
}
}
递归下降解析器:
class ExpressionParser {
private final List<String> tokens;
private int pos = 0;
// expr → term ('OR' term)*
Expression parse() {
Expression result = parseTerm();
while (pos < tokens.size() && tokens.get(pos).equalsIgnoreCase("OR")) {
pos++;
result = new OrExpression(result, parseTerm());
}
return result;
}
// term → factor ('AND' factor)*
private Expression parseTerm() {
Expression result = parseFactor();
while (pos < tokens.size() && tokens.get(pos).equalsIgnoreCase("AND")) {
pos++;
result = new AndExpression(result, parseFactor());
}
return result;
}
// factor → 'key op value' | '(' expr ')'
private Expression parseFactor() {
if (tokens.get(pos).equals("(")) {
pos++; // 跳过 '('
Expression result = parse(); // 递归解析括号内
pos++; // 跳过 ')'
return result;
}
String key = tokens.get(pos++);
String op = tokens.get(pos++);
String val = tokens.get(pos++);
return new ComparisonExpression(key, op, val);
}
}
使用:
List<Map<String, Object>> orders = List.of(
Map.of("id", "ORD-001", "status", "PAID", "amount", 1500, "region", "EAST"),
Map.of("id", "ORD-002", "status", "PENDING", "amount", 500, "region", "WEST"),
Map.of("id", "ORD-003", "status", "PAID", "amount", 800, "region", "EAST")
);
String expr = "status = 'PAID' AND amount > 1000";
Expression ast = new ExpressionParser(tokenize(expr)).parse();
for (Map<String, Object> order : orders) {
if (ast.interpret(order)) {
System.out.println("匹配: " + order.get("id"));
}
}
// 输出: 匹配: ORD-001
4.8 访问者模式:订单多格式导出
场景:订单有3种类型(普通/团购/预售),需要支持3种导出格式(PDF/Excel/JSON),每种格式对每种订单的导出逻辑都不同。3×3=9种组合。
// Element 接口
interface Order {
String getOrderId();
void accept(OrderVisitor visitor); // 关键:accept 方法
}
// 三种订单
class NormalOrder implements Order {
// ... 字段省略
public void accept(OrderVisitor visitor) {
visitor.visit(this); // 第二次分派:根据visitor的实际类型调对应的visit
}
}
class GroupBuyOrder implements Order {
public void accept(OrderVisitor visitor) {
visitor.visit(this);
}
}
class PreSaleOrder implements Order {
public void accept(OrderVisitor visitor) {
visitor.visit(this);
}
}
// Visitor 接口:每种订单一个方法
interface OrderVisitor {
void visit(NormalOrder order);
void visit(GroupBuyOrder order);
void visit(PreSaleOrder order);
}
// 三种导出格式
class PdfExportVisitor implements OrderVisitor {
public void visit(NormalOrder order) { System.out.println("[PDF] 普通订单 " + order.getOrderId()); }
public void visit(GroupBuyOrder order) { System.out.println("[PDF] 团购订单 " + order.getOrderId()); }
public void visit(PreSaleOrder order) { System.out.println("[PDF] 预售订单 " + order.getOrderId()); }
}
class ExcelExportVisitor implements OrderVisitor { /* ... */ }
class JsonExportVisitor implements OrderVisitor { /* ... */ }
使用:
List<Order> orders = List.of(
new NormalOrder("ORD-001", "张三", 1299.00),
new GroupBuyOrder("ORD-002", "李四", 899.00, 50),
new PreSaleOrder("ORD-003", "赵六", 4999.00, "2026-09-01")
);
// 切换导出格式只需换 Visitor
OrderVisitor pdfVisitor = new PdfExportVisitor();
for (Order order : orders) {
order.accept(pdfVisitor); // 双分派自动定位到正确的方法
}
双分派机制:
order.accept(visitor)
↓ 第一次分派:根据 order 的实际类型(NormalOrder)
↓ 调用 NormalOrder.accept()
↓ visitor.visit(this)
↓ 第二次分派:根据 visitor 的实际类型(PdfExportVisitor)
↓ 调用 PdfExportVisitor.visit(NormalOrder)
↓ 精确定位到唯一的方法
何时用访问者:数据结构类型固定(不常加新类型),但操作经常变化(常加新导出格式)。如果经常加新订单类型,每个 Visitor 接口都要改,反而麻烦。
4.9 命令/模板方法/备忘录/迭代器/中介者
这5种模式篇幅原因不展开完整代码,一句话总结:
| 模式 | 场景 | 核心思想 | Spring映射 |
|---|---|---|---|
| 命令 Command | 订单操作(创建/取消/退款)+ 撤销 | 把请求封装成对象,可排队、可撤销 | Runnable、JdbcTemplate |
| 模板方法 Template | 订单处理流程(标准/团购/预售) | 父类定骨架,子类重写步骤 | JdbcTemplate、RestTemplate |
| 备忘录 Memento | 订单状态快照与回滚 | 保存对象状态,可恢复 | WebFlux checkpoint |
| 迭代器 Iterator | 订单列表分页遍历 | 统一遍历接口,不暴露内部结构 | Iterator、Stream |
| 中介者 Mediator | 电商模块协调(库存/支付/物流) | 多对多通信→通过中介者 | DispatcherServlet |
五、结构型模式(7种)—— 对象怎么组合
| 模式 | 场景 | 核心思想 | 与谁容易混淆 |
|---|---|---|---|
| 代理 Proxy | 用户服务权限/缓存/日志 | 控制访问 | 装饰器(都是持有引用,但意图不同) |
| 装饰器 Decorator | 订单价格叠加 | 包装增强功能 | 代理 |
| 适配器 Adapter | 第三方支付SDK适配 | 转换接口 | 外观(都是简化调用) |
| 外观 Facade | 支付入口统一接口 | 简化子系统 | 适配器 |
| 桥接 Bridge | 消息发送(渠道×格式) | 两个维度独立变化 | 策略(都是委托,但桥接是结构型) |
| 组合 Composite | 组织架构树 | 统一处理树形结构 | — |
| 享元 Flyweight | 商品信息共享池 | 共享细粒度对象 | — |
结构型模式的选择:想控制访问用代理,想增强功能用装饰器,想转换接口用适配器,想简化调用用外观,想拆分维度用桥接,想处理树用组合,想省内存用享元。
六、易混淆模式对比
6.1 策略 vs 状态
| 策略模式 | 状态模式 | |
|---|---|---|
| 谁决定 | 外部(Context.setStrategy) | 状态自己(State内部setState) |
| 变化频率 | 一次请求内不变 | 每次操作后可能变 |
| 典型场景 | 选一种折扣算法 | 订单状态流转 |
| 结构 | 几乎一样 | 几乎一样 |
6.2 装饰器 vs 代理
| 装饰器模式 | 代理模式 | |
|---|---|---|
| 意图 | 增强功能 | 控制访问 |
| 谁创建被包装对象 | 调用方传入 | 代理自己创建/管理 |
| 典型场景 | 价格叠加优惠 | 权限/缓存/远程代理 |
| 结构 | 几乎一样 | 几乎一样 |
6.3 适配器 vs 外观
| 适配器模式 | 外观模式 | |
|---|---|---|
| 转换对象 | 一个接口 | 一组子系统 |
| 目的 | 兼容旧接口 | 简化调用 |
| 典型场景 | 第三方SDK适配 | 支付入口统一调用 |
6.4 工厂方法 vs 抽象工厂
| 工厂方法 | 抽象工厂 | |
|---|---|---|
| 创建数量 | 一个产品 | 一族产品 |
| 扩展方式 | 加工厂子类 | 加工厂实现 |
| 典型场景 | 日志记录器 | 云服务商资源族 |
6.5 观察者 vs 中介者
| 观察者模式 | 中介者模式 | |
|---|---|---|
| 通信方向 | 一对多广播 | 多对多通过中介 |
| 耦合方式 | 发布者不知道订阅者 | 各方只知道中介者 |
| 典型场景 | 事件通知 | UI组件协调 |
6.6 命令 vs 备忘录
| 命令模式 | 备忘录模式 | |
|---|---|---|
| 撤销方式 | 执行反向操作 | 恢复历史快照 |
| 存储内容 | 操作命令 | 对象完整状态 |
| 典型场景 | 订单操作撤销 | 订单状态回滚 |
七、设计原则对照
| 原则 | 含义 | 体现的模式 |
|---|---|---|
| 开闭原则 (OCP) | 对扩展开放,对修改关闭 | 策略、观察者、访问者 |
| 单一职责 (SRP) | 一个类只做一件事 | 代理、装饰器、外观 |
| 里氏替换 (LSP) | 子类能替换父类 | 工厂方法、策略 |
| 依赖倒置 (DIP) | 依赖抽象不依赖具体 | 工厂方法、桥接、策略 |
| 接口隔离 (ISP) | 接口最小化 | 适配器、外观 |
| 迪米特法则 (LoD) | 最少知道原则 | 外观、中介者 |
八、Spring框架中的设计模式
学设计模式最实际的意义是——你看Spring源码时能看懂。
| 设计模式 | Spring中的体现 |
|---|---|
| 单例 | @Component、@Service 默认单例 |
| 工厂方法 | BeanFactory、FactoryBean |
| 抽象工厂 | ApplicationContext 体系 |
| 代理 | AOP(@Transactional、@Async、@Cacheable) |
| 模板方法 | JdbcTemplate、RestTemplate、TransactionTemplate |
| 观察者 | ApplicationEvent + ApplicationListener |
| 责任链 | HandlerInterceptorChain |
| 策略 | Resource 接口(ClassPathResource/FileSystemResource) |
| 装饰器 | TransactionAwareCacheDecorator、HttpServletRequestWrapper |
| 适配器 | HandlerAdapter(适配不同Controller类型) |
| 外观 | JdbcTemplate(封装JDBC复杂操作) |
| 组合 | CompositeView、MenuComponent |
| 享元 | Flyweight 模式的字符串常量池 |
九、学习路径建议
第一阶段:先吃透5个高频模式(1周)
策略模式 → 工厂方法 → 单例 → 观察者 → 代理
这5个在Spring中用得最多,学完之后看Spring源码会有"原来如此"的感觉。
第二阶段:创建型+结构型补全(1周)
工厂方法 → 抽象工厂 → 建造者 → 原型
适配器 → 桥接 → 组合 → 装饰器 → 外观 → 享元
第三阶段:行为型进阶(1周)
责任链 → 命令 → 迭代器 → 中介者 → 备忘录 → 状态 → 模板方法 → 解释器 → 访问者
学习方法
- 先跑代码:每个文件都有
main(),先运行看输出 - 看注释对比:文件头注释里有"不用模式会怎样"的对比
- 改一改:试着新增一种策略/审批人/导出格式,感受开闭原则
- 联系Spring:想想日常用的Spring注解对应哪个模式
完整代码
<!-- pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.devkit</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
编译运行:
# 编译全部
mvn compile
# 运行单个模式
java -cp target/classes com.devkit.patterns.creational.Singleton_DatabaseConnectionPool
java -cp target/classes com.devkit.patterns.behavioral.Strategy_DiscountAndPayment
java -cp target/classes com.devkit.patterns.structural.Proxy_UserService
项目结构:
java-design-patterns/
├── pom.xml
└── src/main/java/com/devkit/patterns/
├── creational/ 5个文件
├── behavioral/ 11个文件
└── structural/ 7个文件
总结
| 维度 | 说明 |
|---|---|
| 模式不是目的 | 解决问题才是。别为了用模式而用模式 |
| 先有问题再选模式 | 遇到if-else堆积 → 考虑策略;遇到审批流 → 考虑责任链 |
| 不要过度设计 | 3个if-else不需要策略模式,直接写更清晰 |
| 模式之间不是孤立的 | Spring AOP用代理+责任链+模板方法,实际项目经常组合使用 |
| 模式是语言 | 和团队沟通时,说"这里用策略模式"比说"搞个接口搞几个实现类运行时切换"高效得多 |
如果觉得有帮助,点个赞收个藏,有问题欢迎评论区交流。
更多推荐



所有评论(0)