目录
[一、 创建型模式:解决对象创建的复杂问题](#一、 创建型模式:解决对象创建的复杂问题)
[1. 单例模式:全局配置管理(无人售货柜项目)](#1. 单例模式:全局配置管理(无人售货柜项目))
[2. 工厂方法模式:支付方式适配(电商项目)](#2. 工厂方法模式:支付方式适配(电商项目))
解决方案:工厂方法模式(定义支付工厂,子类实现具体支付方式)
[步骤 1:定义抽象产品(支付接口)](#步骤 1:定义抽象产品(支付接口))
[步骤 2:实现具体产品(各支付方式)](#步骤 2:实现具体产品(各支付方式))
[步骤 3:定义抽象工厂 + 具体工厂](#步骤 3:定义抽象工厂 + 具体工厂)
[步骤 4:业务层调用(无需关注具体实现)](#步骤 4:业务层调用(无需关注具体实现))
[二、 结构型模式:优化代码结构,降低耦合](#二、 结构型模式:优化代码结构,降低耦合)
[1. 代理模式:接口权限控制(无人售货柜后台)](#1. 代理模式:接口权限控制(无人售货柜后台))
[解决方案:动态代理模式(JDK 动态代理,AOP 思想)](#解决方案:动态代理模式(JDK 动态代理,AOP 思想))
[步骤 1:定义业务接口](#步骤 1:定义业务接口)
[步骤 2:实现目标对象(真实业务逻辑)](#步骤 2:实现目标对象(真实业务逻辑))
[步骤 3:实现代理类(权限校验逻辑)](#步骤 3:实现代理类(权限校验逻辑))
[步骤 4:业务层使用代理](#步骤 4:业务层使用代理)
[2. 装饰器模式:IO 流增强(无人售货柜日志模块)](#2. 装饰器模式:IO 流增强(无人售货柜日志模块))
[步骤 1:定义抽象组件(日志接口)](#步骤 1:定义抽象组件(日志接口))
[步骤 2:实现基础组件(本地文件日志)](#步骤 2:实现基础组件(本地文件日志))
[步骤 3:实现装饰器(增强功能)](#步骤 3:实现装饰器(增强功能))
[步骤 4:业务层组合装饰器](#步骤 4:业务层组合装饰器)
[三、 行为型模式:规范对象交互,提升逻辑灵活性](#三、 行为型模式:规范对象交互,提升逻辑灵活性)
[1. 策略模式:订单价格计算(电商项目)](#1. 策略模式:订单价格计算(电商项目))
[步骤 1:定义策略接口](#步骤 1:定义策略接口)
[步骤 2:实现具体策略](#步骤 2:实现具体策略)
[步骤 3:策略上下文(统一调用入口)](#步骤 3:策略上下文(统一调用入口))
[步骤 4:业务层调用](#步骤 4:业务层调用)
[2. 命令模式:设备指令下发(无人售货柜项目)](#2. 命令模式:设备指令下发(无人售货柜项目))
解决方案:命令模式(封装指令为对象,解耦指令发送者与执行者)
[步骤 1:定义抽象命令](#步骤 1:定义抽象命令)
[步骤 2:实现具体命令](#步骤 2:实现具体命令)
[步骤 3:实现命令调用者(指令管理器)](#步骤 3:实现命令调用者(指令管理器))
[步骤 4:控制器调用](#步骤 4:控制器调用)
[四、 设计模式选型核心思路](#四、 设计模式选型核心思路)
[五、 总结](#五、 总结)
设计模式的价值在于解决真实业务场景中的耦合、扩展、复用问题 。本文结合电商订单、无人售货柜、支付系统 等真实项目场景,拆解常用设计模式的落地思路与代码实现,聚焦 "为什么用 ""怎么用 ""解决什么问题" 三大核心。
一、 创建型模式:解决对象创建的复杂问题
创建型模式的核心目标是封装对象创建细节,让调用方无需关注初始化逻辑,提升创建灵活性。
1. 单例模式:全局配置管理(无人售货柜项目)
场景痛点
无人售货柜项目中,DeviceConfig 包含设备的串口配置、网络参数、货道信息等全局配置,全系统需共享同一实例,且初始化时需从本地文件加载配置,避免重复 IO 操作。
解决方案:懒汉式单例(双重检查锁,线程安全)
java
运行
/**
* 售货柜设备配置(单例模式)
* 核心:全局唯一实例 + 延迟初始化 + 线程安全
*/
public class DeviceConfig {
// 1. 私有静态变量(volatile 禁止指令重排)
private static volatile DeviceConfig instance;
// 配置参数
private String serialPort; // 串口地址
private int baudRate; // 波特率
private Map<Integer, String> channelMap; // 货道映射
// 2. 私有构造器(禁止外部new)
private DeviceConfig() {
// 从本地配置文件加载参数(实际项目用Properties/YAML)
loadConfigFromFile();
}
// 3. 公共静态方法(双重检查锁,懒加载)
public static DeviceConfig getInstance() {
if (instance == null) { // 第一次检查:避免不必要的锁
synchronized (DeviceConfig.class) { // 加锁
if (instance == null) { // 第二次检查:防止多线程重复创建
instance = new DeviceConfig();
}
}
}
return instance;
}
// 加载配置文件
private void loadConfigFromFile() {
// 实际逻辑:读取config/device.properties
this.serialPort = "/dev/ttyUSB0";
this.baudRate = 9600;
this.channelMap = new HashMap<>();
channelMap.put(1, "可乐");
channelMap.put(2, "薯片");
}
// getter方法
public String getSerialPort() { return serialPort; }
public Map<Integer, String> getChannelMap() { return channelMap; }
}
应用效果
- 全系统共享同一配置实例,避免重复加载文件导致的性能损耗;
- 延迟初始化:首次调用
getInstance()才加载配置,减少项目启动时间; - 线程安全:双重检查锁保证多线程环境下不会创建多个实例。
框架关联
Spring 容器中的 默认 Bean 是单例模式 ,底层通过 DefaultSingletonBeanRegistry 实现,原理与上述案例一致。
2. 工厂方法模式:支付方式适配(电商项目)
场景痛点
电商订单支付模块需支持微信支付、支付宝、银联 三种方式,后续可能新增 "云闪付"。若直接在业务代码中用 if-else 判断支付类型,会导致代码臃肿、扩展困难(新增支付方式需修改原有代码,违反开闭原则)。
解决方案:工厂方法模式(定义支付工厂,子类实现具体支付方式)
步骤 1:定义抽象产品(支付接口)
java
运行
/**
* 抽象支付产品
*/
public interface Payment {
// 发起支付
String pay(String orderId, BigDecimal amount);
// 查询支付状态
boolean queryPayStatus(String orderId);
}
步骤 2:实现具体产品(各支付方式)
java
运行
// 微信支付实现
public class WechatPayment implements Payment {
@Override
public String pay(String orderId, BigDecimal amount) {
// 调用微信支付SDK
return "微信支付下单成功,订单号:" + orderId + ",金额:" + amount;
}
@Override
public boolean queryPayStatus(String orderId) {
return true; // 模拟查询结果
}
}
// 支付宝支付实现
public class AlipayPayment implements Payment {
@Override
public String pay(String orderId, BigDecimal amount) {
return "支付宝支付下单成功,订单号:" + orderId + ",金额:" + amount;
}
@Override
public boolean queryPayStatus(String orderId) {
return true;
}
}
步骤 3:定义抽象工厂 + 具体工厂
java
运行
/**
* 抽象支付工厂
*/
public interface PaymentFactory {
Payment createPayment();
}
// 微信支付工厂
public class WechatPaymentFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WechatPayment();
}
}
// 支付宝支付工厂
public class AlipayPaymentFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new AlipayPayment();
}
}
步骤 4:业务层调用(无需关注具体实现)
java
运行
@Service
public class OrderPayService {
// 根据支付类型获取工厂,创建支付实例
public String doPay(String orderId, BigDecimal amount, String payType) {
PaymentFactory factory;
switch (payType) {
case "WECHAT":
factory = new WechatPaymentFactory();
break;
case "ALIPAY":
factory = new AlipayPaymentFactory();
break;
default:
throw new IllegalArgumentException("不支持的支付类型");
}
Payment payment = factory.createPayment();
return payment.pay(orderId, amount);
}
}
应用效果
- 开闭原则 :新增 "云闪付" 只需添加
CloudPayment和CloudPaymentFactory,无需修改原有业务代码; - 职责单一:每种支付方式的逻辑封装在独立类中,便于维护和测试;
- 解耦 :业务层只依赖
Payment接口,不依赖具体实现类。
二、 结构型模式:优化代码结构,降低耦合
结构型模式的核心目标是通过类 / 对象的组合,解决结构臃肿、扩展困难的问题。
1. 代理模式:接口权限控制(无人售货柜后台)
场景痛点
无人售货柜后台的 DeviceManageService 包含设备重启、货道修改、参数配置 等敏感接口,需对调用方进行权限校验 (如只有管理员角色才能调用)。若直接在接口中添加权限逻辑,会导致业务逻辑与权限逻辑耦合,且重复代码多。
解决方案:动态代理模式(JDK 动态代理,AOP 思想)
步骤 1:定义业务接口
java
运行
/**
* 设备管理接口(被代理接口)
*/
public interface DeviceManageService {
void restartDevice(String deviceId); // 重启设备
void updateChannel(String deviceId, Map<Integer, String> channelMap); // 修改货道
}
步骤 2:实现目标对象(真实业务逻辑)
java
运行
/**
* 真实设备管理服务
*/
@Service
public class DeviceManageServiceImpl implements DeviceManageService {
@Override
public void restartDevice(String deviceId) {
System.out.println("重启设备:" + deviceId);
// 实际逻辑:发送重启指令到硬件
}
@Override
public void updateChannel(String deviceId, Map<Integer, String> channelMap) {
System.out.println("修改设备货道:" + deviceId + ",货道映射:" + channelMap);
}
}
步骤 3:实现代理类(权限校验逻辑)
java
运行
/**
* 权限代理类(JDK 动态代理)
* 核心:在不修改目标对象的前提下,增强方法功能
*/
public class PermissionProxy implements InvocationHandler {
// 目标对象(被代理的真实对象)
private final Object target;
public PermissionProxy(Object target) {
this.target = target;
}
/**
* 代理方法:增强目标方法的执行逻辑
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 前置增强:权限校验
checkPermission();
// 2. 执行目标方法
Object result = method.invoke(target, args);
// 3. 后置增强:记录操作日志
logOperation(method.getName(), args);
return result;
}
// 权限校验逻辑
private void checkPermission() {
// 实际逻辑:从 ThreadLocal 获取当前用户角色
String role = UserContextHolder.getCurrentUser().getRole();
if (!"ADMIN".equals(role)) {
throw new SecurityException("无权限操作设备!");
}
}
// 日志记录逻辑
private void logOperation(String methodName, Object[] args) {
System.out.println("执行设备操作:" + methodName + ",参数:" + Arrays.toString(args));
}
// 获取代理对象的工具方法
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new PermissionProxy(target)
);
}
}
步骤 4:业务层使用代理
java
运行
@Configuration
public class DeviceConfig {
@Bean
public DeviceManageService deviceManageService() {
// 创建真实对象
DeviceManageServiceImpl target = new DeviceManageServiceImpl();
// 生成代理对象并返回
return (DeviceManageService) PermissionProxy.getProxy(target);
}
}
// 控制器调用
@RestController
@RequestMapping("/device")
public class DeviceController {
@Autowired
private DeviceManageService deviceManageService;
@PostMapping("/restart/{deviceId}")
public String restart(@PathVariable String deviceId) {
deviceManageService.restartDevice(deviceId);
return "操作成功";
}
}
应用效果
- 解耦:权限校验、日志记录与业务逻辑完全分离,符合单一职责原则;
- 可复用:代理逻辑可复用在其他需要权限控制的接口上;
- 无侵入:无需修改目标对象的代码,只需通过代理增强功能。
框架关联
Spring AOP 的底层就是动态代理模式 ,@Transactional、@Cacheable 等注解的实现原理与上述案例一致。
2. 装饰器模式:IO 流增强(无人售货柜日志模块)
场景痛点
无人售货柜的日志模块需要记录设备操作日志,要求:
- 日志内容需格式化(包含时间、设备 ID、操作类型);
- 日志需同时写入本地文件 + 上传到云端;
- 后续可能新增 "加密日志" 功能。若直接写一个
LogService包含所有逻辑,会导致功能耦合、扩展困难。
解决方案:装饰器模式(动态给日志对象添加功能)
步骤 1:定义抽象组件(日志接口)
java
运行
/**
* 抽象日志组件
*/
public interface Logger {
void log(String deviceId, String content);
}
步骤 2:实现基础组件(本地文件日志)
java
运行
/**
* 基础日志组件:写入本地文件
*/
public class FileLogger implements Logger {
@Override
public void log(String deviceId, String content) {
// 实际逻辑:写入本地文件 /logs/device-{deviceId}.log
System.out.println("【本地日志】设备:" + deviceId + ",内容:" + content);
}
}
步骤 3:实现装饰器(增强功能)
java
运行
/**
* 装饰器抽象类:遵循抽象组件接口,持有组件引用
*/
public abstract class LoggerDecorator implements Logger {
protected Logger logger;
public LoggerDecorator(Logger logger) {
this.logger = logger;
}
}
// 装饰器1:格式化日志(添加时间戳)
public class FormatLoggerDecorator extends LoggerDecorator {
public FormatLoggerDecorator(Logger logger) {
super(logger);
}
@Override
public void log(String deviceId, String content) {
// 格式化内容:[时间] 设备ID - 内容
String formatContent = String.format("[%s] %s - %s",
LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
deviceId, content);
// 调用被装饰者的方法
logger.log(deviceId, formatContent);
}
}
// 装饰器2:上传云端日志
public class CloudLoggerDecorator extends LoggerDecorator {
public CloudLoggerDecorator(Logger logger) {
super(logger);
}
@Override
public void log(String deviceId, String content) {
// 1. 先执行被装饰者的逻辑(写入本地)
logger.log(deviceId, content);
// 2. 新增功能:上传到云端
uploadToCloud(deviceId, content);
}
private void uploadToCloud(String deviceId, String content) {
System.out.println("【云端日志】设备:" + deviceId + ",内容:" + content);
}
}
步骤 4:业务层组合装饰器
java
运行
@Service
public class DeviceLogService {
private final Logger logger;
// 构造器注入:组合装饰器
public DeviceLogService() {
// 基础组件:本地日志
Logger baseLogger = new FileLogger();
// 装饰器1:格式化
Logger formatLogger = new FormatLoggerDecorator(baseLogger);
// 装饰器2:上传云端
this.logger = new CloudLoggerDecorator(formatLogger);
}
public void recordLog(String deviceId, String content) {
logger.log(deviceId, content);
}
}
// 调用示例
deviceLogService.recordLog("DEVICE001", "货道1补货完成");
// 输出结果:
// 【本地日志】设备:DEVICE001,内容:[2025-12-26T15:30:00] DEVICE001 - 货道1补货完成
// 【云端日志】设备:DEVICE001,内容:[2025-12-26T15:30:00] DEVICE001 - 货道1补货完成
应用效果
- 功能动态组合 :按需组合装饰器(如只需要本地日志则不加
CloudLoggerDecorator); - 开闭原则 :新增 "加密日志" 只需添加
EncryptLoggerDecorator,无需修改原有代码; - 职责单一:每个装饰器只负责一个增强功能,便于维护。
框架关联
JDK 的 IO 流是装饰器模式的经典实现 ,如 BufferedInputStream 装饰 FileInputStream,实现缓冲功能。
三、 行为型模式:规范对象交互,提升逻辑灵活性
行为型模式的核心目标是定义对象间的通信方式,解决行为协调、职责分配的问题。
1. 策略模式:订单价格计算(电商项目)
场景痛点
电商订单结算时,需根据用户等级、优惠券、促销活动计算最终价格:
- 普通用户:原价;
- VIP 用户:9 折;
- 新用户:满 100 减 20;
- 叠加优惠券:再减固定金额。若用
if-else实现,会导致逻辑混乱、扩展困难(新增优惠类型需修改结算方法)。
解决方案:策略模式(封装不同优惠策略,动态切换)
步骤 1:定义策略接口
java
运行
/**
* 订单优惠策略接口
*/
public interface DiscountStrategy {
/**
* 计算优惠后价格
* @param order 订单信息
* @return 优惠后金额
*/
BigDecimal calculate(Order order);
/**
* 获取策略类型
*/
String getType();
}
步骤 2:实现具体策略
java
运行
// 策略1:普通用户(无优惠)
public class NormalUserStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(Order order) {
return order.getTotalAmount();
}
@Override
public String getType() {
return "NORMAL";
}
}
// 策略2:VIP用户(9折)
public class VipUserStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(Order order) {
return order.getTotalAmount().multiply(new BigDecimal("0.9"));
}
@Override
public String getType() {
return "VIP";
}
}
// 策略3:新用户(满100减20)
public class NewUserStrategy implements DiscountStrategy {
@Override
public BigDecimal calculate(Order order) {
BigDecimal amount = order.getTotalAmount();
return amount.compareTo(new BigDecimal("100")) >= 0 ? amount.subtract(new BigDecimal("20")) : amount;
}
@Override
public String getType() {
return "NEW";
}
}
步骤 3:策略上下文(统一调用入口)
java
运行
/**
* 策略上下文:封装策略选择逻辑,提供统一调用入口
*/
@Service
public class DiscountContext {
// 策略容器:Spring 启动时自动注入所有策略实现类
private final Map<String, DiscountStrategy> strategyMap;
// 构造器注入:Spring 会自动将所有 DiscountStrategy 实现类放入 Map
public DiscountContext(List<DiscountStrategy> strategies) {
this.strategyMap = new HashMap<>();
for (DiscountStrategy strategy : strategies) {
strategyMap.put(strategy.getType(), strategy);
}
}
/**
* 计算订单最终价格
*/
public BigDecimal calculateFinalPrice(Order order) {
// 1. 获取用户类型对应的策略
String userType = order.getUser().getType();
DiscountStrategy strategy = strategyMap.getOrDefault(userType, new NormalUserStrategy());
// 2. 计算基础优惠价格
BigDecimal discountAmount = strategy.calculate(order);
// 3. 叠加优惠券优惠
if (order.getCoupon() != null) {
discountAmount = discountAmount.subtract(order.getCoupon().getAmount());
}
// 4. 价格不能小于0
return discountAmount.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : discountAmount;
}
}
步骤 4:业务层调用
java
运行
@Service
public class OrderService {
@Autowired
private DiscountContext discountContext;
public BigDecimal checkout(Order order) {
return discountContext.calculateFinalPrice(order);
}
}
应用效果
- 消除 if-else :策略选择逻辑由
strategyMap自动完成,代码更简洁; - 灵活扩展 :新增 "黑钻用户 8 折" 策略,只需添加
BlackDiamondStrategy类,无需修改原有代码; - 便于测试:每个策略可单独测试,降低测试复杂度。
2. 命令模式:设备指令下发(无人售货柜项目)
场景痛点
无人售货柜后台需向设备下发多种指令:
- 货道库存查询指令;
- 设备重启指令;
- 商品价格更新指令;要求:
- 指令可异步执行(无需等待设备响应);
- 指令可记录日志(便于排查问题);
- 后续可新增指令类型(如 "清库存指令")。
解决方案:命令模式(封装指令为对象,解耦指令发送者与执行者)
步骤 1:定义抽象命令
java
运行
/**
* 设备指令抽象命令
*/
public interface DeviceCommand {
// 执行指令
void execute();
// 获取指令ID
String getCommandId();
}
步骤 2:实现具体命令
java
运行
/**
* 具体命令1:库存查询指令
*/
public class StockQueryCommand implements DeviceCommand {
private final String deviceId;
private final DeviceClient deviceClient; // 设备通信客户端(执行者)
private final String commandId;
public StockQueryCommand(String deviceId, DeviceClient deviceClient) {
this.deviceId = deviceId;
this.deviceClient = deviceClient;
this.commandId = "CMD_" + System.currentTimeMillis();
}
@Override
public void execute() {
// 调用执行者的方法:下发查询指令
String result = deviceClient.sendQueryStockCommand(deviceId);
// 记录指令执行日志
System.out.println("执行库存查询指令:" + commandId + ",设备:" + deviceId + ",结果:" + result);
}
@Override
public String getCommandId() {
return commandId;
}
}
/**
* 具体命令2:价格更新指令
*/
public class PriceUpdateCommand implements DeviceCommand {
private final String deviceId;
private final DeviceClient deviceClient;
private final Map<Integer, BigDecimal> priceMap; // 货道-价格映射
private final String commandId;
public PriceUpdateCommand(String deviceId, DeviceClient deviceClient, Map<Integer, BigDecimal> priceMap) {
this.deviceId = deviceId;
this.deviceClient = deviceClient;
this.priceMap = priceMap;
this.commandId = "CMD_" + System.currentTimeMillis();
}
@Override
public void execute() {
String result = deviceClient.sendUpdatePriceCommand(deviceId, priceMap);
System.out.println("执行价格更新指令:" + commandId + ",设备:" + deviceId + ",结果:" + result);
}
@Override
public String getCommandId() {
return commandId;
}
}
步骤 3:实现命令调用者(指令管理器)
java
运行
/**
* 命令调用者:设备指令管理器
* 核心:触发命令执行,无需关注命令具体逻辑
*/
@Service
public class DeviceCommandInvoker {
@Autowired
private DeviceClient deviceClient;
// 线程池:异步执行指令
private final ExecutorService executor = Executors.newFixedThreadPool(5);
/**
* 下发库存查询指令
*/
public String sendStockQueryCommand(String deviceId) {
DeviceCommand command = new StockQueryCommand(deviceId, deviceClient);
executor.submit(command::execute); // 异步执行
return command.getCommandId();
}
/**
* 下发价格更新指令
*/
public String sendPriceUpdateCommand(String deviceId, Map<Integer, BigDecimal> priceMap) {
DeviceCommand command = new PriceUpdateCommand(deviceId, deviceClient, priceMap);
executor.submit(command::execute);
return command.getCommandId();
}
}
步骤 4:控制器调用
java
运行
@RestController
@RequestMapping("/device/command")
public class DeviceCommandController {
@Autowired
private DeviceCommandInvoker commandInvoker;
@PostMapping("/stock/{deviceId}")
public String queryStock(@PathVariable String deviceId) {
String commandId = commandInvoker.sendStockQueryCommand(deviceId);
return "指令已下发,ID:" + commandId;
}
@PostMapping("/price/{deviceId}")
public String updatePrice(@PathVariable String deviceId, @RequestBody Map<Integer, BigDecimal> priceMap) {
String commandId = commandInvoker.sendPriceUpdateCommand(deviceId, priceMap);
return "指令已下发,ID:" + commandId;
}
}
应用效果
- 解耦 :指令发送者(控制器)与执行者(
DeviceClient)完全分离,发送者无需知道指令如何执行; - 异步化:通过线程池实现指令异步执行,提升系统响应速度;
- 可扩展 :新增 "清库存指令" 只需添加
StockClearCommand类,无需修改调用者代码; - 可追踪:每个指令有唯一 ID,便于日志记录和问题排查。
四、 设计模式选型核心思路
- 先解决问题,再谈模式:不要为了用模式而用模式,简单场景(如查询用户)直接写代码即可;
- 遵循设计原则 :所有模式的本质是落地七大设计原则,违背原则的模式使用都是 "过度设计";
- 组合使用模式 :实际项目中,模式往往是组合使用的(如工厂模式 + 策略模式 、代理模式 + 装饰器模式);
- 参考框架源码:Spring、MyBatis 等框架是设计模式的最佳实践教材,阅读源码能加深对模式的理解。
五、 总结
设计模式不是 "银弹",而是解决特定问题的工具。在实际项目中,判断是否使用模式的核心标准是:
- 是否降低了耦合?
- 是否提升了扩展性?
- 是否便于维护和测试?
掌握设计模式的关键,不是死记硬背代码结构,而是理解模式背后的设计思想 ------高内聚、低耦合、开闭原则,这样才能在面对复杂业务场景时,灵活选择合适的模式。