设计模式是解决特定场景软件设计问题的最佳实践,核心价值是解耦、复用、可维护。本文按三大类梳理23种模式,每个模式配套「核心逻辑图+Java实战案例+代码片段」,兼顾面试考点与开发落地,避免纯抽象定义。
一、创建型模式(5种)
核心目标:封装对象创建过程,解耦创建与使用,提高灵活性。
1. 工厂模式(3种变体)
1.1 简单工厂模式
核心逻辑:一个工厂类根据参数,动态创建不同产品实例,隐藏创建细节。
![[Pasted_image_20260122202216.png]]
Java实战案例:后端数据处理场景中,根据数据类型参数,创建不同实体类对象封装数据。
// 产品接口:统一数据模型规范
public interface DataBean {}
// 具体产品1:天气数据实体
public class WeatherBean implements DataBean {
private String temp; // 温度
// getter/setter、构造方法
public String getTemp() { return temp; }
public void setTemp(String temp) { this.temp = temp; }
}
// 具体产品2:城市数据实体
public class CityBean implements DataBean {
private String cityName; // 城市名
// getter/setter、构造方法
public String getCityName() { return cityName; }
public void setCityName(String cityName) { this.cityName = cityName; }
}
// 简单工厂类:根据参数创建对应产品
public class WeatherFactory {
public static final int TYPE_WEATHER = 1;
public static final int TYPE_CITY = 2;
public static DataBean createDataBean(int type) {
switch (type) {
case TYPE_WEATHER:
return new WeatherBean(); // 创建天气对象
case TYPE_CITY:
return new CityBean(); // 创建城市对象
default:
throw new IllegalArgumentException("无效类型");
}
}
}
// 客户端使用(纯Java应用,非Android)
public class DataProcessor {
public static void main(String[] args) {
// 无需关注创建细节,通过工厂获取对象
DataBean weatherBean = WeatherFactory.createDataBean(WeatherFactory.TYPE_WEATHER);
DataBean cityBean = WeatherFactory.createDataBean(WeatherFactory.TYPE_CITY);
// 模拟数据封装
if (weatherBean instanceof WeatherBean) {
((WeatherBean) weatherBean).setTemp("25℃");
}
if (cityBean instanceof CityBean) {
((CityBean) cityBean).setCityName("北京");
}
}
}
优缺点&场景:优点是简化创建,客户端无需关注细节;缺点是新增产品需改工厂(违背开闭原则)。适合产品少、变更少的纯Java场景(如固定数据模型创建、工具类实例生成)。
1.2 工厂方法模式
核心逻辑:定义抽象工厂接口,每个具体产品对应一个具体工厂,新增产品只需加工厂,符合开闭原则。
![[Pasted_image_20260122202200.png]]
Java实战案例:后端数据持久层场景,针对MySQL、Oracle两种不同数据库,分别创建对应的数据封装对象工厂,适配不同数据源的数据格式规范。
// 产品接口:统一数据模型规范
public interface DataBean {}
// 具体产品1:MySQL数据源封装对象
public class MysqlDataBean implements DataBean {
private String mysqlData;
// getter/setter、构造方法
public String getMysqlData() { return mysqlData; }
public void setMysqlData(String mysqlData) { this.mysqlData = mysqlData; }
}
// 具体产品2:Oracle数据源封装对象
public class OracleDataBean implements DataBean {
private String oracleData;
// getter/setter、构造方法
public String getOracleData() { return oracleData; }
public void setOracleData(String oracleData) { this.oracleData = oracleData; }
}
// 抽象工厂接口
public interface DataBeanFactory {
DataBean createDataBean();
}
// 具体工厂1:创建MySQL数据对象
public class MysqlDataFactory implements DataBeanFactory {
@Override
public DataBean createDataBean() {
return new MysqlDataBean();
}
}
// 具体工厂2:创建Oracle数据对象
public class OracleDataFactory implements DataBeanFactory {
@Override
public DataBean createDataBean() {
return new OracleDataBean();
}
}
// 客户端使用(纯Java后端服务)
public class DataService {
public static void main(String[] args) {
// 切换工厂即可适配不同数据源,无需修改核心逻辑
DataBeanFactory factory = new MysqlDataFactory();
DataBean mysqlData = factory.createDataBean();
factory = new OracleDataFactory();
DataBean oracleData = factory.createDataBean();
// 模拟数据封装
((MysqlDataBean) mysqlData).setMysqlData("MySQL数据库数据");
((OracleDataBean) oracleData).setOracleData("Oracle数据库数据");
}
}
优缺点&场景:优点是符合开闭原则,扩展灵活;缺点是类数量膨胀(产品+工厂)。适合纯Java中产品多、独立性强的场景(如多数据源对象创建、不同格式数据解析器生成)。
1.3 抽象工厂模式
核心逻辑:抽象工厂创建一组相关的产品族,而非单一产品,保证产品族兼容性。
![[Pasted_image_20260122202135.png]]
Java实战案例:后端国际化数据处理服务,区分国内/国外数据产品族(配套的基础数据+扩展数据封装),适配不同地区的字段规范与展示需求。
// 产品族1:天气数据接口
public interface WeatherBean {
void showWeatherInfo();
}
// 产品族2:城市数据接口
public interface CityBean {
void showCityInfo();
}
// 具体产品:国内天气/城市
public class ChinaWeatherBean implements WeatherBean {
private String temp;
@Override
public void showWeatherInfo() {
System.out.println("国内温度:" + temp);
}
// getter/setter、构造方法
}
public class ChinaCityBean implements CityBean {
private String cityName;
@Override
public void showCityInfo() {
System.out.println("国内城市:" + cityName);
}
// getter/setter、构造方法
}
// 具体产品:国外天气/城市
public class ForeignWeatherBean implements WeatherBean {
private String temp;
private String unit; // 国外温度单位(华氏度)
@Override
public void showWeatherInfo() {
System.out.println("Foreign Temperature:" + temp + unit);
}
// getter/setter、构造方法
}
public class ForeignCityBean implements CityBean {
private String cityName;
private String country; // 国外城市所属国家
@Override
public void showCityInfo() {
System.out.println("Foreign City:" + cityName + "," + country);
}
// getter/setter、构造方法
}
// 抽象工厂:定义产品族创建方法
public interface WeatherProductFactory {
WeatherBean createWeatherBean();
CityBean createCityBean();
}
// 具体工厂1:国内产品族
public class ChinaWeatherFactory implements WeatherProductFactory {
@Override
public WeatherBean createWeatherBean() {
return new ChinaWeatherBean();
}
@Override
public CityBean createCityBean() {
return new ChinaCityBean();
}
}
// 具体工厂2:国外产品族
public class ForeignWeatherFactory implements WeatherProductFactory {
@Override
public WeatherBean createWeatherBean() {
return new ForeignWeatherBean();
}
@Override
public CityBean createCityBean() {
return new ForeignCityBean();
}
}
// 客户端使用(纯Java后端服务)
public class InternationalDataProcessor {
public static void main(String[] args) {
// 获取国内产品族
WeatherProductFactory chinaFactory = new ChinaWeatherFactory();
WeatherBean chinaWeather = chinaFactory.createWeatherBean();
CityBean chinaCity = chinaFactory.createCityBean();
// 模拟数据赋值与展示
((ChinaWeatherBean) chinaWeather).setTemp("25℃");
((ChinaCityBean) chinaCity).setCityName("北京");
chinaWeather.showWeatherInfo();
chinaCity.showCityInfo();
}
}
优缺点&场景:优点是保证产品族兼容,扩展产品族灵活;缺点是扩展单产品困难。适合纯Java中需一组关联产品的场景(如国际化配套数据创建、多版本功能模块生成)。
2. 单例模式
核心逻辑:确保类仅一个实例,提供全局访问点,避免重复创建消耗资源。
Java实战案例:后端日志工具类(全局统一记录系统操作日志)、数据库连接池(控制连接数量避免资源耗尽),这类场景需强制保证全局唯一实例,杜绝重复创建导致的资源浪费与数据不一致问题。
// 单例模式:饿汉式(线程安全,加载即初始化)
public class LoggerUtil {
// 私有静态实例,类加载时直接初始化
private static final LoggerUtil INSTANCE = new LoggerUtil();
// 私有构造方法,禁止外部实例化
private LoggerUtil() {}
// 公共静态方法,提供全局访问点
public static LoggerUtil getInstance() {
return INSTANCE;
}
// 核心业务方法:记录日志
public void log(String message) {
System.out.println("[" + System.currentTimeMillis() + "] " + message);
}
}
// 单例模式:懒汉式(双重检查锁,线程安全+延迟加载)
public class DbConnectionPool {
// 私有静态实例,volatile保证可见性与禁止指令重排
private static volatile DbConnectionPool INSTANCE;
// 私有构造方法,初始化连接池资源
private DbConnectionPool() {
// 模拟初始化数据库连接
System.out.println("初始化数据库连接池,创建10个连接");
}
// 双重检查锁,保证高效且线程安全
public static DbConnectionPool getInstance() {
if (INSTANCE == null) {
synchronized (DbConnectionPool.class) {
if (INSTANCE == null) {
INSTANCE = new DbConnectionPool();
}
}
}
return INSTANCE;
}
// 核心业务方法:获取连接
public String getConnection() {
return "获取数据库连接:" + System.currentTimeMillis();
}
}
// 客户端使用
public class ServerApplication {
public static void main(String[] args) {
// 日志工具类使用
LoggerUtil logger = LoggerUtil.getInstance();
logger.log("系统启动成功");
// 数据库连接池使用
DbConnectionPool pool = DbConnectionPool.getInstance();
String connection = pool.getConnection();
System.out.println(connection);
// 验证单例唯一性
LoggerUtil logger2 = LoggerUtil.getInstance();
System.out.println("日志工具类实例是否唯一:" + (logger == logger2));
DbConnectionPool pool2 = DbConnectionPool.getInstance();
System.out.println("连接池实例是否唯一:" + (pool == pool2));
}
}
核心逻辑图:
![[Pasted_image_20260122202413.png]]
优缺点&场景:优点是控制实例数量、节约资源、保证数据一致;缺点是扩展性差,不适用于多线程高并发场景(需额外处理线程安全)。适合纯Java中需全局唯一实例的场景(日志工具、连接池、配置管理器等)。
3. 建造者模式
核心逻辑:将复杂对象的构建与表示分离,通过分步构建、统一组装,生成不同配置的对象,解决复杂对象创建时参数过多、配置混乱问题。
![[Pasted_image_20260122202501.png]]
Java实战案例:后端订单生成场景,订单包含基础信息(编号、金额)、扩展信息(收货地址、支付方式),通过建造者分步构建不同类型订单(普通订单、VIP订单)。
// 复杂产品:订单类
public class Order {
private String orderNo; // 订单编号
private double amount; // 订单金额
private String address; // 收货地址(可选)
private String payType; // 支付方式(可选)
// 私有构造,仅允许建造者访问
private Order(Builder builder) {
this.orderNo = builder.orderNo;
this.amount = builder.amount;
this.address = builder.address;
this.payType = builder.payType;
}
// 静态建造者内部类
public static class Builder {
// 必传参数
private final String orderNo;
private final double amount;
// 可选参数
private String address;
private String payType;
// 构造方法接收必传参数
public Builder(String orderNo, double amount) {
this.orderNo = orderNo;
this.amount = amount;
}
// 分步设置可选参数,返回自身实现链式调用
public Builder setAddress(String address) {
this.address = address;
return this;
}
public Builder setPayType(String payType) {
this.payType = payType;
return this;
}
// 构建产品
public Order build() {
return new Order(this);
}
}
// 业务方法
public void showOrderInfo() {
System.out.println("订单编号:" + orderNo + ",金额:" + amount + ",地址:" + address + ",支付方式:" + payType);
}
}
// 客户端使用(无需指挥者,简化版建造者,实际可加指挥者统一构建逻辑)
public class OrderService {
public static void main(String[] args) {
// 构建普通订单(含必选+部分可选参数)
Order normalOrder = new Order.Builder("ORDER001", 299.9)
.setAddress("北京市朝阳区")
.setPayType("支付宝")
.build();
// 构建VIP订单(不同配置)
Order vipOrder = new Order.Builder("ORDER002", 5999.9)
.setAddress("上海市浦东新区")
.setPayType("微信支付")
.build();
normalOrder.showOrderInfo();
vipOrder.showOrderInfo();
}
}
优缺点&场景:优点是分离构建与表示、支持链式调用、参数配置清晰;缺点是类结构复杂(多建造者类)。适合纯Java中复杂对象创建(订单、用户信息、配置类等参数多的场景)。
4. 原型模式
核心逻辑:通过复制已有实例(原型)创建新对象,避免重复执行复杂初始化逻辑,提高创建效率,分为浅克隆与深克隆。
暂时无法在豆包文档外展示此内容
Java实战案例:后端批量生成报表场景,报表基础模板(标题、格式)固定,仅数据不同,通过克隆原型模板快速生成多个报表,减少重复初始化。
import java.io.*;
// 原型类:报表模板(实现Cloneable接口支持克隆)
public class ReportTemplate implements Cloneable, Serializable {
private String title; // 报表标题(基本类型)
private ReportData data; // 报表数据(引用类型)
// 构造方法:初始化模板
public ReportTemplate(String title, ReportData data) {
this.title = title;
this.data = data;
// 模拟复杂初始化逻辑(如读取模板文件、初始化格式)
System.out.println("报表模板初始化完成(耗时操作)");
}
// 浅克隆:默认clone(),引用类型共享
@Override
protected ReportTemplate clone() throws CloneNotSupportedException {
return (ReportTemplate) super.clone();
}
// 深克隆:通过序列化实现,引用类型独立
public ReportTemplate deepClone() throws IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ReportTemplate) ois.readObject();
}
// getter/setter
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public ReportData getData() { return data; }
public void setData(ReportData data) { this.data = data; }
}
// 引用类型:报表数据(需实现Serializable支持深克隆)
class ReportData implements Serializable {
private String content;
public ReportData(String content) {
this.content = content;
}
// getter/setter
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
}
// 客户端使用
public class ReportService {
public static void main(String[] args) throws Exception {
// 1. 创建原型实例(仅初始化一次,耗时操作)
ReportData prototypeData = new ReportData("原型报表数据");
ReportTemplate prototype = new ReportTemplate("月度报表模板", prototypeData);
// 2. 浅克隆
ReportTemplate shallowClone = prototype.clone();
shallowClone.setTitle("浅克隆月度报表");
shallowClone.getData().setContent("浅克隆报表数据");
// 验证:原型与浅克隆的引用类型共享
System.out.println("原型数据与浅克隆数据是否相同:" + (prototype.getData() == shallowClone.getData()));
// 3. 深克隆
ReportTemplate deepClone = prototype.deepClone();
deepClone.setTitle("深克隆月度报表");
deepClone.getData().setContent("深克隆报表数据");
// 验证:原型与深克隆的引用类型独立
System.out.println("原型数据与深克隆数据是否相同:" + (prototype.getData() == deepClone.getData()));
}
}
优缺点&场景:优点是避免重复初始化、提高创建效率;缺点是深克隆实现复杂。适合纯Java中对象初始化成本高、需批量创建相似对象的场景(报表、缓存对象、复杂POJO)。
二、结构型模式(7种)
核心目标:通过类或对象的组合,优化系统结构,实现功能复用与解耦,适配不同场景的结构需求。
1. 适配器模式
核心逻辑:将一个类的接口转换成客户端期望的另一个接口,解决不同接口间的兼容性问题,分为类适配器与对象适配器。
![[Pasted_image_20260122202614.png]]
Java实战案例:后端系统集成场景,旧系统支付接口(Adaptee)与新系统支付标准接口(Target)不兼容,通过适配器转换接口,实现旧接口复用。
// 目标接口:新系统支付标准接口
public interface Payment {
void pay(double amount); // 新接口方法:支付指定金额
}
// 适配者:旧系统支付接口(接口不兼容,需适配)
public class OldPaymentSystem {
// 旧接口方法:参数与目标接口不同
public void doPay(String money) {
System.out.println("旧系统支付:" + money + "元");
}
}
// 适配器:对象适配器(推荐,通过组合适配者实现)
public class PaymentAdapter implements Payment {
private OldPaymentSystem oldPaymentSystem;
// 注入适配者实例
public PaymentAdapter(OldPaymentSystem oldPaymentSystem) {
this.oldPaymentSystem = oldPaymentSystem;
}
// 转换接口,适配旧系统方法
@Override
public void pay(double amount) {
// 类型转换:新接口double -> 旧接口String
String money = String.valueOf(amount);
oldPaymentSystem.doPay(money);
}
}
// 客户端使用(新系统调用)
public class PaymentService {
public static void main(String[] args) {
// 初始化旧系统支付
OldPaymentSystem oldSystem = new OldPaymentSystem();
// 通过适配器适配新接口
Payment payment = new PaymentAdapter(oldSystem);
// 新系统按标准接口调用
payment.pay(299.9);
}
}
优缺点&场景:优点是复用旧接口、解耦适配逻辑;缺点是增加系统复杂度。适合纯Java中接口不兼容场景(系统集成、旧代码复用、第三方组件适配)。
2. 装饰器模式
核心逻辑:动态给对象添加额外功能,不改变原类结构,通过组合替代继承,实现功能的灵活扩展。
![[Pasted_image_20260122202651.png]]
Java实战案例:后端日志打印场景,基础日志仅输出文本,通过装饰器动态添加时间戳、日志级别、异常堆栈等功能,灵活组合扩展。
// 抽象组件:日志接口
public interface Logger {
void log(String message);
}
// 具体组件:基础日志(核心功能)
public class BasicLogger implements Logger {
@Override
public void log(String message) {
System.out.print(message); // 仅输出文本
}
}
// 装饰器抽象类(实现日志接口,持有组件实例)
public abstract class LoggerDecorator implements Logger {
protected Logger logger;
public LoggerDecorator(Logger logger) {
this.logger = logger;
}
@Override
public abstract void log(String message);
}
// 具体装饰器1:添加时间戳
public class TimeLoggerDecorator extends LoggerDecorator {
public TimeLoggerDecorator(Logger logger) {
super(logger);
}
@Override
public void log(String message) {
String time = "[" + System.currentTimeMillis() + "] ";
logger.log(time + message); // 先加时间戳,再调用核心功能
}
}
// 具体装饰器2:添加日志级别
public class LevelLoggerDecorator extends LoggerDecorator {
private String level;
public LevelLoggerDecorator(Logger logger, String level) {
super(logger);
this.level = level;
}
@Override
public void log(String message) {
String levelStr = "[" + level + "] ";
logger.log(levelStr + message); // 先加级别,再调用核心/装饰后功能
}
}
// 客户端使用(组合装饰器)
public class LoggerService {
public static void main(String[] args) {
// 基础日志
Logger basicLogger = new BasicLogger();
// 装饰器组合:基础日志 + 时间戳 + 错误级别
Logger errorLogger = new LevelLoggerDecorator(new TimeLoggerDecorator(basicLogger), "ERROR");
// 装饰后功能
errorLogger.log("系统异常:数据库连接失败");
}
}
优缺点&场景:优点是动态扩展功能、组合灵活;缺点是装饰器类过多易复杂。适合纯Java中需灵活扩展功能的场景(日志、IO流、缓存增强等)。
3. 代理模式
核心逻辑:为目标对象提供一个代理对象,由代理对象控制对目标对象的访问,可在访问前后添加额外逻辑(增强、控制),分为静态代理与动态代理。
![[Pasted_image_20260122202723.png]]
Java实战案例:后端接口权限控制场景,通过代理对目标接口方法进行权限校验,无权限则拒绝访问,不修改目标接口代码。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 目标接口:用户服务
public interface UserService {
void queryUser(String userId);
void deleteUser(String userId);
}
// 目标对象:用户服务实现
public class UserServiceImpl implements UserService {
@Override
public void queryUser(String userId) {
System.out.println("查询用户信息,用户ID:" + userId);
}
@Override
public void deleteUser(String userId) {
System.out.println("删除用户,用户ID:" + userId);
}
}
// 动态代理:JDK动态代理(基于接口)
public class AuthProxyHandler implements InvocationHandler {
private Object target; // 目标对象
public AuthProxyHandler(Object target) {
this.target = target;
}
// 代理逻辑:权限校验
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:权限校验(仅管理员可删除,查询无限制)
String methodName = method.getName();
if ("deleteUser".equals(methodName)) {
String role = "user"; // 模拟当前用户角色,实际从会话获取
if (!"admin".equals(role)) {
throw new RuntimeException("无权限删除用户");
}
}
// 调用目标对象方法
Object result = method.invoke(target, args);
// 后置增强:日志记录(可选)
System.out.println("方法调用完成:" + methodName);
return result;
}
// 获取代理对象
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new AuthProxyHandler(target)
);
}
}
// 客户端使用
public class UserServiceClient {
public static void main(String[] args) {
// 目标对象
UserService userService = new UserServiceImpl();
// 获取代理对象
UserService proxy = (UserService) AuthProxyHandler.getProxy(userService);
// 调用查询方法(无权限校验)
proxy.queryUser("1001");
// 调用删除方法(普通用户无权限,抛异常)
try {
proxy.deleteUser("1001");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
优缺点&场景:优点是解耦增强逻辑与核心业务、支持动态扩展;缺点是动态代理仅支持接口。适合纯Java中需控制访问、增强功能的场景(权限控制、事务管理、日志监控)。
4. 桥接模式
核心逻辑:将抽象部分与实现部分分离,使两者可独立扩展,通过组合替代继承,解决多维度变化导致的类爆炸问题。 ![[Pasted_image_20260122202737.png]]
Java实战案例:后端消息发送场景,消息类型(普通消息、紧急消息)与发送方式(短信、邮件)为两个独立维度,通过桥接模式实现多维度灵活组合。
// 实现维度:消息发送方式接口(Implementor)
public interface MessageSender {
void send(String content); // 发送消息核心方法
}
// 具体实现1:短信发送
public class SmsSender implements MessageSender {
@Override
public void send(String content) {
System.out.println("短信发送:" + content);
}
}
// 具体实现2:邮件发送
public class EmailSender implements MessageSender {
@Override
public void send(String content) {
System.out.println("邮件发送:" + content);
}
}
// 抽象维度:消息抽象类(Abstraction)
public abstract class Message {
protected MessageSender sender; // 持有实现维度引用(桥接核心)
// 注入发送方式,解耦抽象与实现
public Message(MessageSender sender) {
this.sender = sender;
}
public abstract void sendMessage(String content); // 抽象发送方法
}
// 扩展抽象1:普通消息
public class NormalMessage extends Message {
public NormalMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content) {
System.out.println("【普通消息】");
sender.send(content); // 调用实现维度方法
}
}
// 扩展抽象2:紧急消息
public class EmergencyMessage extends Message {
public EmergencyMessage(MessageSender sender) {
super(sender);
}
@Override
public void sendMessage(String content) {
System.out.println("【紧急消息】(重复发送3次)");
// 增强逻辑:紧急消息重复发送
for (int i = 0; i < 3; i++) {
sender.send(content);
}
}
}
// 客户端使用
public class MessageService {
public static void main(String[] args) {
// 组合1:普通消息+短信发送
Message normalSms = new NormalMessage(new SmsSender());
normalSms.sendMessage("您有一条新通知");
// 组合2:紧急消息+邮件发送
Message emergencyEmail = new EmergencyMessage(new EmailSender());
emergencyEmail.sendMessage("系统异常,请立即处理");
}
}
优缺点&场景:优点是分离多维度变化、扩展灵活、减少类数量;缺点是增加系统复杂度,需理解抽象与实现分离逻辑。适合纯Java中多维度独立变化的场景(消息发送、日志输出、支付组合等)。
5. 组合模式
核心逻辑:将对象组合成树形结构,使单个对象(叶子)和组合对象(容器)具有一致的访问接口,统一处理树形结构中的所有元素。
暂时无法在豆包文档外展示此内容
Java实战案例:后端组织架构管理场景,部门(容器)可包含子部门(容器)和员工(叶子),通过组合模式统一查询部门/员工信息、计算总人数。
// 抽象组件:组织节点(Component)
public abstract class OrganizationNode {
protected String name;
public OrganizationNode(String name) {
this.name = name;
}
// 统一方法:显示节点信息
public abstract void showInfo();
// 容器节点需实现的方法(叶子节点空实现)
public void addNode(OrganizationNode node) {
throw new UnsupportedOperationException("叶子节点不支持添加操作");
}
public void removeNode(OrganizationNode node) {
throw new UnsupportedOperationException("叶子节点不支持删除操作");
}
// 统计节点数量(叶子为1,容器为自身+子节点总数)
public abstract int countNode();
}
// 叶子节点:员工
public class Employee extends OrganizationNode {
private String position; // 职位
public Employee(String name, String position) {
super(name);
this.position = position;
}
@Override
public void showInfo() {
System.out.println("员工:" + name + ",职位:" + position);
}
@Override
public int countNode() {
return 1; // 员工自身为1个节点
}
}
// 容器节点:部门
public class Department extends OrganizationNode {
private List<OrganizationNode> children = new ArrayList<>(); // 子节点列表
public Department(String name) {
super(name);
}
@Override
public void showInfo() {
System.out.println("部门:" + name);
// 遍历子节点,调用统一方法
for (OrganizationNode node : children) {
node.showInfo();
}
}
@Override
public void addNode(OrganizationNode node) {
children.add(node);
}
@Override
public void removeNode(OrganizationNode node) {
children.remove(node);
}
@Override
public int countNode() {
int count = 1; // 自身为1个节点
// 累加子节点数量
for (OrganizationNode node : children) {
count += node.countNode();
}
return count;
}
}
// 客户端使用
public class OrganizationService {
public static void main(String[] args) {
// 构建组织架构树
OrganizationNode company = new Department("总公司");
OrganizationNode techDept = new Department("技术部");
OrganizationNode hrDept = new Department("人事部");
// 技术部添加员工
techDept.addNode(new Employee("张三", "后端开发"));
techDept.addNode(new Employee("李四", "前端开发"));
// 人事部添加员工
hrDept.addNode(new Employee("王五", "人事专员"));
// 总公司添加部门
company.addNode(techDept);
company.addNode(hrDept);
// 统一显示所有节点信息
company.showInfo();
// 统一统计总人数
System.out.println("组织总人数:" + company.countNode());
}
}
优缺点&场景:优点是统一访问接口、简化树形结构操作、易于扩展;缺点是叶子与容器职责模糊,部分操作需区分类型。适合纯Java中树形结构场景(组织架构、文件系统、菜单管理等)。
6. 外观模式
核心逻辑:为子系统中的一组接口提供一个统一的高层接口,简化客户端与子系统的交互,隐藏子系统复杂度,降低耦合。
![[Pasted_image_20260122202846.png]]
Java实战案例:后端用户注册场景,注册流程需调用用户服务、日志服务、邮件服务三个子系统,通过外观模式封装流程,客户端一键调用。
// 子系统1:用户服务(创建用户)
public class UserService {
public void createUser(String username, String password) {
System.out.println("创建用户:" + username);
}
}
// 子系统2:日志服务(记录日志)
public class LogService {
public void log(String message) {
System.out.println("日志记录:" + message);
}
}
// 子系统3:邮件服务(发送注册邮件)
public class EmailService {
public void sendRegisterEmail(String email) {
System.out.println("向" + email + "发送注册成功邮件");
}
}
// 外观类:注册外观(封装子系统交互)
public class RegisterFacade {
// 依赖子系统
private UserService userService = new UserService();
private LogService logService = new LogService();
private EmailService emailService = new EmailService();
// 封装注册流程,提供统一接口
public void register(String username, String password, String email) {
userService.createUser(username, password);
logService.log("用户" + username + "注册成功");
emailService.sendRegisterEmail(email);
}
}
// 客户端使用
public class RegisterClient {
public static void main(String[] args) {
// 无需直接调用三个子系统,仅调用外观类方法
RegisterFacade registerFacade = new RegisterFacade();
registerFacade.register("zhangsan", "123456", "zhangsan@xxx.com");
}
}
优缺点&场景:优点是简化客户端操作、隐藏子系统复杂度、降低耦合;缺点是外观类可能成为全能类,违背单一职责。适合纯Java中多子系统协同场景(注册流程、订单提交、系统启动等)。
7. 享元模式
核心逻辑:运用共享技术复用大量相似对象,减少内存占用,将对象的公共属性(内部状态)与可变属性(外部状态)分离,共享内部状态。
![[Pasted_image_20260122202901.png]]
Java实战案例:后端棋子游戏场景,棋盘上大量棋子(黑棋、白棋)仅颜色(内部状态)固定,位置(外部状态)可变,通过享元模式复用棋子对象,减少内存消耗。
// 享元接口
public interface ChessPiece {
// 传入外部状态(位置),执行操作
void display(int x, int y);
}
// 具体享元:黑棋(内部状态固定)
public class BlackChess implements ChessPiece {
// 内部状态:颜色(共享,固定)
private final String color = "黑色";
@Override
public void display(int x, int y) {
System.out.println("放置" + color + "棋子,位置:(" + x + "," + y + ")");
}
}
// 具体享元:白棋(内部状态固定)
public class WhiteChess implements ChessPiece {
// 内部状态:颜色(共享,固定)
private final String color = "白色";
@Override
public void display(int x, int y) {
System.out.println("放置" + color + "棋子,位置:(" + x + "," + y + ")");
}
}
// 享元工厂:管理享元对象缓存与创建
public class ChessPieceFactory {
// 缓存池:存储共享享元对象
private static Map<String, ChessPiece> chessPool = new HashMap<>();
// 获取享元对象,不存在则创建并缓存
public static ChessPiece getChessPiece(String color) {
ChessPiece chessPiece = chessPool.get(color);
if (chessPiece == null) {
switch (color) {
case "黑色":
chessPiece = new BlackChess();
break;
case "白色":
chessPiece = new WhiteChess();
break;
default:
throw new IllegalArgumentException("无效棋子颜色");
}
chessPool.put(color, chessPiece);
System.out.println("创建" + color + "棋子并缓存");
} else {
System.out.println("复用缓存中的" + color + "棋子");
}
return chessPiece;
}
}
// 客户端使用
public class ChessGame {
public static void main(String[] args) {
// 多次获取黑棋、白棋,仅首次创建,后续复用
ChessPiece black1 = ChessPieceFactory.getChessPiece("黑色");
black1.display(1, 1);
ChessPiece black2 = ChessPieceFactory.getChessPiece("黑色");
black2.display(1, 3);
ChessPiece white1 = ChessPieceFactory.getChessPiece("白色");
white1.display(2, 2);
ChessPiece white2 = ChessPieceFactory.getChessPiece("白色");
white2.display(2, 4);
}
}
优缺点&场景:优点是减少对象创建、节省内存;缺点是分离内部/外部状态,增加系统复杂度。适合纯Java中大量相似对象场景(游戏棋子、字符缓存、图标资源等)。
三、行为型模式(11种)
核心目标:关注对象间的交互与职责分配,优化算法、请求传递、状态管理等行为逻辑,实现行为复用与解耦。
1. 策略模式
核心逻辑:定义一系列算法,将每个算法封装成独立类,使算法可动态切换,且与使用算法的客户端解耦。
![[Pasted_image_20260122202942.png]]
Java实战案例:后端支付场景,支持支付宝、微信、银联三种支付算法,通过策略模式动态切换支付方式,便于扩展新支付渠道。
// 策略接口:支付策略
public interface PaymentStrategy {
void pay(double amount); // 支付算法核心方法
}
// 具体策略1:支付宝支付
public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("通过支付宝支付:" + amount + "元");
}
}
// 具体策略2:微信支付
public class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("通过微信支付:" + amount + "元");
}
}
// 具体策略3:银联支付
public class UnionPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("通过银联支付:" + amount + "元");
}
}
// 上下文:支付上下文(封装策略切换与调用)
public class PaymentContext {
private PaymentStrategy strategy;
// 注入策略,支持动态切换
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
// 调用策略算法
public void executePay(double amount) {
if (strategy == null) {
throw new RuntimeException("请选择支付方式");
}
strategy.pay(amount);
}
}
// 客户端使用
public class PaymentClient {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
double amount = 399.9;
// 切换策略:支付宝支付
context.setStrategy(new AlipayStrategy());
context.executePay(amount);
// 切换策略:微信支付
context.setStrategy(new WechatPayStrategy());
context.executePay(amount);
}
}
优缺点&场景:优点是算法可动态切换、扩展灵活、解耦算法与客户端;缺点是策略类数量多,客户端需了解策略差异。适合纯Java中多种算法可选、需动态切换的场景(支付方式、排序算法、日志级别等)。
2. 模板方法模式
核心逻辑:定义一个算法的骨架(模板方法),将算法中的可变步骤延迟到子类实现,固定算法流程,灵活定制具体步骤。
暂时无法在豆包文档外展示此内容
Java实战案例:后端数据导出场景,导出流程(准备数据、导出文件、记录日志)固定,导出格式(Excel、CSV)可变,通过模板方法模式定制不同导出方式。
// 抽象类:数据导出模板
public abstract class DataExportTemplate {
// 模板方法:定义算法骨架(流程固定,不可重写)
public final void exportData() {
prepareData(); // 固定步骤1:准备数据
doExport(); // 可变步骤2:导出文件(子类实现)
logExport(); // 固定步骤3:记录日志
}
// 固定步骤1:准备数据
private void prepareData() {
System.out.println("准备数据:查询数据库并封装");
}
// 可变步骤2:导出文件(抽象方法,子类定制)
protected abstract void doExport();
// 固定步骤3:记录日志
private void logExport() {
System.out.println("日志记录:数据导出完成");
}
}
// 子类1:Excel导出
public class ExcelExport extends DataExportTemplate {
@Override
protected void doExport() {
System.out.println("导出为Excel文件,格式:.xlsx");
}
}
// 子类2:CSV导出
public class CsvExport extends DataExportTemplate {
@Override
protected void doExport() {
System.out.println("导出为CSV文件,格式:.csv");
}
}
// 客户端使用
public class ExportClient {
public static void main(String[] args) {
// Excel导出
DataExportTemplate excelExport = new ExcelExport();
excelExport.exportData();
System.out.println("-------------------");
// CSV导出
DataExportTemplate csvExport = new CsvExport();
csvExport.exportData();
}
}
优缺点&场景:优点是固定算法流程、复用公共代码、扩展灵活;缺点是子类过多易导致类膨胀,模板方法不可修改。适合纯Java中算法流程固定、具体步骤可变的场景(数据导出、接口调用、业务流程处理等)。
3. 观察者模式
核心逻辑:定义对象间的一对多依赖关系,当被观察者(主题)状态变化时,自动通知所有观察者并更新,实现松耦合的通知机制。
暂时无法在豆包文档外展示此内容
Java实战案例:后端订单状态通知场景,订单(被观察者)状态变更为“已支付”“已发货”时,自动通知短信服务、日志服务、库存服务(观察者)执行对应操作。
import java.util.ArrayList;
import java.util.List;
// 观察者接口
public interface Observer {
// 接收通知并更新(参数为被观察者状态)
void update(String orderNo, String status);
}
// 具体观察者1:短信服务
public class SmsObserver implements Observer {
@Override
public void update(String orderNo, String status) {
System.out.println("短信通知:订单" + orderNo + "状态变更为" + status);
}
}
// 具体观察者2:日志服务
public class LogObserver implements Observer {
@Override
public void update(String orderNo, String status) {
System.out.println("日志记录:订单" + orderNo + "状态变更为" + status);
}
}
// 具体观察者3:库存服务
public class StockObserver implements Observer {
@Override
public void update(String orderNo, String status) {
if ("已支付".equals(status)) {
System.out.println("库存操作:订单" + orderNo + "已支付,扣减对应库存");
}
}
}
// 被观察者:订单主题
public class OrderSubject {
// 观察者列表
private List<Observer> observers = new ArrayList<>();
private String orderNo;
private String status;
public OrderSubject(String orderNo) {
this.orderNo = orderNo;
}
// 注册观察者
public void registerObserver(Observer observer) {
observers.add(observer);
}
// 移除观察者
public void removeObserver(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(orderNo, status);
}
}
// 设置订单状态(状态变化时触发通知)
public void setStatus(String status) {
this.status = status;
notifyObservers();
}
}
// 客户端使用
public class OrderClient {
public static void main(String[] args) {
// 创建被观察者:订单
OrderSubject order = new OrderSubject("ORDER003");
// 注册观察者
order.registerObserver(new SmsObserver());
order.registerObserver(new LogObserver());
order.registerObserver(new StockObserver());
// 变更订单状态,自动通知所有观察者
System.out.println("=== 订单状态变更为:已支付 ===");
order.setStatus("已支付");
System.out.println("=== 订单状态变更为:已发货 ===");
order.setStatus("已发货");
}
}
优缺点&场景:优点是解耦主题与观察者、支持动态注册/移除、通知机制灵活;缺点是观察者过多时通知效率低,可能产生循环依赖。适合纯Java中一对多通知场景(订单状态、消息推送、事件监听等)。
4. 迭代器模式
核心逻辑:提供一种统一遍历聚合对象的方式,分离聚合对象的存储与遍历职责,使遍历逻辑独立于聚合结构,支持多种遍历方式。
暂时无法在豆包文档外展示此内容
Java实战案例:后端自定义容器场景,实现一个专属数据集合,通过迭代器模式提供统一遍历接口,支持正向、反向两种遍历方式,不暴露集合内部存储结构。
// 迭代器接口
public interface MyIterator<T> {
boolean hasNext(); // 是否有下一个元素
T next(); // 获取下一个元素
}
// 聚合接口
public interface MyAggregate<T> {
MyIterator<T> createIterator(); // 创建迭代器
}
// 具体迭代器:正向迭代
public class ForwardIterator<T> implements MyIterator<T> {
private MyList<T> list; // 聚合对象
private int index = 0; // 遍历索引
public ForwardIterator(MyList<T> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public T next() {
if (hasNext()) {
return list.get(index++);
}
throw new NoSuchElementException("无更多元素");
}
}
// 具体迭代器:反向迭代
public class ReverseIterator<T> implements MyIterator<T> {
private MyList<T> list;
private int index;
public ReverseIterator(MyList<T> list) {
this.list = list;
this.index = list.size() - 1;
}
@Override
public boolean hasNext() {
return index >= 0;
}
@Override
public T next() {
if (hasNext()) {
return list.get(index--);
}
throw new NoSuchElementException("无更多元素");
}
}
// 具体聚合:自定义列表
public class MyList<T> implements MyAggregate<T> {
private Object[] elements; // 内部存储结构
private int size = 0;
public MyList(int capacity) {
elements = new Object[capacity];
}
// 添加元素
public void add(T element) {
if (size < elements.length) {
elements[size++] = element;
} else {
throw new IndexOutOfBoundsException("列表已满");
}
}
// 获取指定索引元素(仅供迭代器访问,不对外暴露)
public T get(int index) {
if (index >= 0 && index < size) {
return (T) elements[index];
}
throw new IndexOutOfBoundsException("索引越界");
}
// 获取元素数量
public int size() {
return size;
}
// 创建正向迭代器
@Override
public MyIterator<T> createIterator() {
return new ForwardIterator<>(this);
}
// 额外提供反向迭代器创建方法
public MyIterator<T> createReverseIterator() {
return new ReverseIterator<>(this);
}
}
// 客户端使用
public class IteratorClient {
public static void main(String[] args) {
MyList<String> list = new MyList<>(5);
list.add("Java");
list.add("设计模式");
list.add("迭代器模式");
// 正向遍历
MyIterator<String> forwardIt = list.createIterator();
System.out.println("正向遍历:");
while (forwardIt.hasNext()) {
System.out.println(forwardIt.next());
}
// 反向遍历
MyIterator<String> reverseIt = list.createReverseIterator();
System.out.println("\n反向遍历:");
while (reverseIt.hasNext()) {
System.out.println(reverseIt.next());
}
}
}
优缺点&场景:优点是分离存储与遍历、支持多种遍历方式、扩展灵活;缺点是增加类数量(迭代器+聚合)。适合纯Java中自定义聚合对象、需统一遍历接口的场景(自定义容器、树形结构遍历、复杂集合遍历)。
5. 责任链模式
核心逻辑:将请求的处理者连成一条链,请求沿链传递,直到有一个处理者处理该请求,避免请求发送者与接收者耦合,动态调整处理链路。 ![[Pasted_image_20260122203151.png]]
Java实战案例:后端接口权限校验场景,请求需经过普通用户校验、管理员校验、超级管理员校验三级链路,不同权限处理不同范围的请求,形成责任链。
// 请求类:封装请求信息
public class AuthRequest {
private String userId; // 用户ID
private String role; // 用户角色
private String requestType; // 请求类型(查询/删除/修改)
public AuthRequest(String userId, String role, String requestType) {
this.userId = userId;
this.role = role;
this.requestType = requestType;
}
// getter
public String getUserId() { return userId; }
public String getRole() { return role; }
public String getRequestType() { return requestType; }
}
// 处理者接口
public abstract class AuthHandler {
protected AuthHandler nextHandler; // 下一个处理者
// 设置下一个处理者,构建责任链
public void setNextHandler(AuthHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 处理请求的抽象方法
public abstract void handleRequest(AuthRequest request);
}
// 具体处理者1:普通用户处理器(仅处理查询请求)
public class UserHandler extends AuthHandler {
@Override
public void handleRequest(AuthRequest request) {
if ("user".equals(request.getRole()) && "查询".equals(request.getRequestType())) {
System.out.println("普通用户" + request.getUserId() + "处理查询请求");
} else if (nextHandler != null) {
// 无法处理,传递给下一个处理者
nextHandler.handleRequest(request);
} else {
throw new RuntimeException("无对应处理器处理请求");
}
}
}
// 具体处理者2:管理员处理器(处理查询、修改请求)
public class AdminHandler extends AuthHandler {
@Override
public void handleRequest(AuthRequest request) {
if ("admin".equals(request.getRole())) {
if ("查询".equals(request.getRequestType()) || "修改".equals(request.getRequestType())) {
System.out.println("管理员" + request.getUserId() + "处理" + request.getRequestType() + "请求");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
throw new RuntimeException("无对应处理器处理请求");
}
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
throw new RuntimeException("无对应处理器处理请求");
}
}
}
// 具体处理者3:超级管理员处理器(处理所有请求)
public class SuperAdminHandler extends AuthHandler {
@Override
public void handleRequest(AuthRequest request) {
if ("superAdmin".equals(request.getRole())) {
System.out.println("超级管理员" + request.getUserId() + "处理" + request.getRequestType() + "请求");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
throw new RuntimeException("无对应处理器处理请求");
}
}
}
// 客户端使用:构建责任链并发起请求
public class AuthClient {
public static void main(String[] args) {
// 创建处理者
AuthHandler userHandler = new UserHandler();
AuthHandler adminHandler = new AdminHandler();
AuthHandler superAdminHandler = new SuperAdminHandler();
// 构建责任链:用户 -> 管理员 -> 超级管理员
userHandler.setNextHandler(adminHandler);
adminHandler.setNextHandler(superAdminHandler);
// 发起不同请求
AuthRequest request1 = new AuthRequest("1001", "user", "查询");
userHandler.handleRequest(request1);
AuthRequest request2 = new AuthRequest("1002", "admin", "修改");
userHandler.handleRequest(request2);
AuthRequest request3 = new AuthRequest("1003", "superAdmin", "删除");
userHandler.handleRequest(request3);
}
}
优缺点&场景:优点是解耦请求与处理者、动态调整链路、支持扩展;缺点是请求可能遍历全链,效率低,链路构建复杂。适合纯Java中多处理者处理同一请求、需动态分配责任的场景(权限校验、日志分级处理、异常处理链路)。
6. 命令模式
核心逻辑:将请求封装成命令对象,使请求的发起者与执行者解耦,支持命令的存储、撤销、队列执行等功能,通过命令对象传递请求。
暂时无法在豆包文档外展示此内容
Java实战案例:后端订单操作场景,将订单的创建、取消、支付操作封装成命令,通过调用者执行命令,支持命令的撤销与日志记录,解耦操作发起与执行逻辑。
// 接收者1:订单服务(执行具体操作)
public class OrderService {
public void createOrder(String orderNo) {
System.out.println("创建订单:" + orderNo);
}
public void cancelOrder(String orderNo) {
System.out.println("取消订单:" + orderNo);
}
}
// 接收者2:支付服务(执行支付操作)
public class PaymentService {
public void payOrder(String orderNo, double amount) {
System.out.println("支付订单" + orderNo + ",金额:" + amount + "元");
}
}
// 命令接口
public interface OrderCommand {
void execute(); // 执行命令
void undo(); // 撤销命令
}
// 具体命令1:创建订单命令
public class CreateOrderCommand implements OrderCommand {
private OrderService orderService;
private String orderNo;
public CreateOrderCommand(OrderService orderService, String orderNo) {
this.orderService = orderService;
this.orderNo = orderNo;
}
@Override
public void execute() {
orderService.createOrder(orderNo);
}
@Override
public void undo() {
orderService.cancelOrder(orderNo); // 撤销创建即取消订单
}
}
// 具体命令2:支付订单命令
public class PayOrderCommand implements OrderCommand {
private PaymentService paymentService;
private String orderNo;
private double amount;
private boolean paid = false; // 支付状态,用于撤销判断
public PayOrderCommand(PaymentService paymentService, String orderNo, double amount) {
this.paymentService = paymentService;
this.orderNo = orderNo;
this.amount = amount;
}
@Override
public void execute() {
if (!paid) {
paymentService.payOrder(orderNo, amount);
paid = true;
}
}
@Override
public void undo() {
if (paid) {
System.out.println("撤销支付订单" + orderNo + ",金额:" + amount + "元(退款)");
paid = false;
}
}
}
// 调用者:命令执行器
public class CommandInvoker {
private OrderCommand command; // 持有当前命令
private List<OrderCommand> commandHistory = new ArrayList<>(); // 命令历史,支持批量撤销
// 设置命令
public void setCommand(OrderCommand command) {
this.command = command;
}
// 执行命令并记录历史
public void executeCommand() {
if (command != null) {
command.execute();
commandHistory.add(command);
}
}
// 撤销上一条命令
public void undoLastCommand() {
if (!commandHistory.isEmpty()) {
OrderCommand lastCommand = commandHistory.remove(commandHistory.size() - 1);
lastCommand.undo();
} else {
throw new RuntimeException("无命令可撤销");
}
}
}
// 客户端使用
public class CommandClient {
public static void main(String[] args) {
// 初始化接收者
OrderService orderService = new OrderService();
PaymentService paymentService = new PaymentService();
// 创建命令
OrderCommand createCommand = new CreateOrderCommand(orderService, "ORDER004");
OrderCommand payCommand = new PayOrderCommand(paymentService, "ORDER004", 299.9);
// 初始化调用者
CommandInvoker invoker = new CommandInvoker();
// 执行创建命令
invoker.setCommand(createCommand);
invoker.executeCommand();
// 执行支付命令
invoker.setCommand(payCommand);
invoker.executeCommand();
// 撤销支付命令
System.out.println("\n撤销上一条命令:");
invoker.undoLastCommand();
// 撤销创建命令
System.out.println("撤销上一条命令:");
invoker.undoLastCommand();
}
}
优缺点&场景:优点是解耦发起者与执行者、支持命令撤销/队列/日志、扩展灵活;缺点是类数量膨胀(命令+接收者)。适合纯Java中需封装请求、支持撤销/日志的场景(订单操作、任务调度、遥控器功能、事务管理)。
7. 状态模式
核心逻辑:将对象的状态封装成独立状态类,对象状态变化时切换对应的状态类,使对象行为随状态动态改变,状态逻辑与对象本身分离。
暂时无法在豆包文档外展示此内容
Java实战案例:后端订单状态管理场景,订单包含“待支付”“已支付”“已发货”“已完成”四种状态,不同状态下执行不同操作(支付、发货、确认收货),状态变化时自动切换行为。
// 上下文:订单类(持有当前状态)
public class OrderContext {
private OrderState currentState;
private String orderNo;
public OrderContext(String orderNo) {
this.orderNo = orderNo;
this.currentState = new PendingPayState(); // 初始状态:待支付
}
// 设置当前状态(状态切换)
public void setState(OrderState state) {
this.currentState = state;
}
// 获取订单号
public String getOrderNo() {
return orderNo;
}
// 委托当前状态执行支付操作
public void pay() {
currentState.pay(this);
}
// 委托当前状态执行发货操作
public void ship() {
currentState.ship(this);
}
// 委托当前状态执行确认收货操作
public void confirmReceive() {
currentState.confirmReceive(this);
}
}
// 状态接口
public interface OrderState {
void pay(OrderContext context); // 支付操作
void ship(OrderContext context); // 发货操作
void confirmReceive(OrderContext context); // 确认收货操作
}
// 具体状态1:待支付
public class PendingPayState implements OrderState {
@Override
public void pay(OrderContext context) {
System.out.println("订单" + context.getOrderNo() + "支付成功");
context.setState(new PaidState()); // 切换状态为已支付
}
@Override
public void ship(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "未支付,无法发货");
}
@Override
public void confirmReceive(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "未支付,无法确认收货");
}
}
// 具体状态2:已支付
public class PaidState implements OrderState {
@Override
public void pay(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "已支付,无需重复支付");
}
@Override
public void ship(OrderContext context) {
System.out.println("订单" + context.getOrderNo() + "发货成功");
context.setState(new ShippedState()); // 切换状态为已发货
}
@Override
public void confirmReceive(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "未发货,无法确认收货");
}
}
// 具体状态3:已发货
public class ShippedState implements OrderState {
@Override
public void pay(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "已支付,无需重复支付");
}
@Override
public void ship(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "已发货,无需重复发货");
}
@Override
public void confirmReceive(OrderContext context) {
System.out.println("订单" + context.getOrderNo() + "确认收货成功");
context.setState(new CompletedState()); // 切换状态为已完成
}
}
// 具体状态4:已完成
public class CompletedState implements OrderState {
@Override
public void pay(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "已完成,无需支付");
}
@Override
public void ship(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "已完成,无需发货");
}
@Override
public void confirmReceive(OrderContext context) {
throw new RuntimeException("订单" + context.getOrderNo() + "已完成,无需重复确认收货");
}
}
// 客户端使用
public class StateClient {
public static void main(String[] args) {
OrderContext order = new OrderContext("ORDER005");
// 执行状态操作,触发状态切换
order.pay(); // 待支付 -> 已支付
order.ship(); // 已支付 -> 已发货
order.confirmReceive(); // 已发货 -> 已完成
// 尝试执行已完成状态不支持的操作
try {
order.pay();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
优缺点&场景:优点是状态逻辑分离、状态切换清晰、扩展灵活;缺点是类数量膨胀(状态类过多)。适合纯Java中对象状态多、行为随状态变化的场景(订单状态、流程状态、设备状态管理)。
8. 备忘录模式
核心逻辑:在不破坏封装的前提下,捕获对象的内部状态并保存,以便后续恢复到该状态,实现对象状态的备份与回滚,分离状态存储与对象本身。
暂时无法在豆包文档外展示此内容
Java实战案例:后端文本编辑器场景,编辑器(发起人)可输入文本,通过备忘录备份当前文本状态,支持回滚到上一版本,管理者负责存储多个备份记录。
// 备忘录:存储文本状态(内部类,仅发起人可访问状态)
public class TextMemento {
// 私有构造,仅发起人可创建
private TextMemento(String content) {
this.content = content;
}
private String content; // 存储的文本状态
// 仅发起人可获取状态(通过内部类访问控制)
private String getContent() {
return content;
}
// 发起人:文本编辑器
public static class TextEditor {
private String currentContent; // 当前文本内容
public void setContent(String content) {
System.out.println("设置文本内容:" + content);
this.currentContent = content;
}
// 创建备忘录,备份当前状态
public TextMemento createMemento() {
return new TextMemento(currentContent);
}
// 恢复状态
public void restoreMemento(TextMemento memento) {
this.currentContent = memento.getContent();
System.out.println("恢复文本内容:" + currentContent);
}
// 获取当前内容
public String getCurrentContent() {
return currentContent;
}
}
}
// 管理者:备忘录管理类(存储多个备份)
public class MementoCaretaker {
private List<TextMemento> mementoList = new ArrayList<>();
// 保存备忘录
public void saveMemento(TextMemento memento) {
mementoList.add(memento);
System.out.println("备份完成,当前备份数:" + mementoList.size());
}
// 获取指定索引的备忘录(回滚到对应版本)
public TextMemento getMemento(int index) {
if (index >= 0 && index < mementoList.size()) {
return mementoList.get(index);
}
throw new IndexOutOfBoundsException("备份索引越界");
}
// 获取上一个版本的备忘录
public TextMemento getLastMemento() {
if (!mementoList.isEmpty()) {
return mementoList.get(mementoList.size() - 1);
}
throw new RuntimeException("无备份记录");
}
}
// 客户端使用
public class MementoClient {
public static void main(String[] args) {
// 初始化发起人、管理者
TextMemento.TextEditor editor = new TextMemento.TextEditor();
MementoCaretaker caretaker = new MementoCaretaker();
// 输入文本并备份
editor.setContent("版本1:设计模式学习");
caretaker.saveMemento(editor.createMemento());
// 继续输入并备份
editor.setContent("版本2:设计模式学习 - 备忘录模式");
caretaker.saveMemento(editor.createMemento());
// 继续输入
editor.setContent("版本3:设计模式学习 - 备忘录模式实战");
System.out.println("当前文本:" + editor.getCurrentContent());
// 回滚到上一版本(版本2)
editor.restoreMemento(caretaker.getLastMemento());
System.out.println("回滚后文本:" + editor.getCurrentContent());
// 回滚到版本1
editor.restoreMemento(caretaker.getMemento(0));
System.out.println("回滚后文本:" + editor.getCurrentContent());
}
}
优缺点&场景:优点是备份与恢复灵活、不破坏封装;缺点是频繁备份消耗内存,备忘录状态过多易冗余。适合纯Java中需保存对象状态、支持回滚的场景(文本编辑、配置备份、事务回滚、游戏存档)。
9. 中介者模式
核心逻辑:定义一个中介者对象,封装多个对象间的交互关系,使对象间不直接通信,通过中介者间接交互,减少对象间耦合,集中管理交互逻辑。
暂时无法在豆包文档外展示此内容
Java实战案例:后端聊天系统场景,用户(同事对象)发送消息时不直接交互,通过聊天中介者转发消息,中介者负责维护用户列表、分发消息,支持一对一、群聊消息转发。
// 中介者接口
public interface ChatMediator {
void registerUser(User user); // 注册用户
void sendMessage(String message, User sender, User receiver); // 一对一消息
void sendGroupMessage(String message, User sender); // 群聊消息
}
// 同事对象:用户
public class User {
private String username;
private ChatMediator mediator;
public User(String username, ChatMediator mediator) {
this.username = username;
this.mediator = mediator;
mediator.registerUser(this); // 注册到中介者
}
// 发送一对一消息
public void sendTo(String message, User receiver) {
System.out.println(username + " 发送给 " + receiver.getUsername() + ":" + message);
mediator.sendMessage(message, this, receiver);
}
// 发送群聊消息
public void sendGroup(String message) {
System.out.println(username + " 发送群聊消息:" + message);
mediator.sendGroupMessage(message, this);
}
// 接收消息
public void receiveMessage(String message, User sender) {
System.out.println(username + " 收到 " + sender.getUsername() + " 的消息:" + message);
}
// getter
public String getUsername() {
return username;
}
}
// 具体中介者:聊天中介
public class ChatMediatorImpl implements ChatMediator {
private List<User> userList = new ArrayList<>(); // 维护用户列表
@Override
public void registerUser(User user) {
if (!userList.contains(user)) {
userList.add(user);
System.out.println("用户 " + user.getUsername() + " 加入聊天");
}
}
@Override
public void sendMessage(String message, User sender, User receiver) {
if (userList.contains(sender) && userList.contains(receiver)) {
receiver.receiveMessage(message, sender);
} else {
throw new RuntimeException("发送者或接收者未加入聊天");
}
}
@Override
public void sendGroupMessage(String message, User sender) {
if (userList.contains(sender)) {
// 转发给除发送者外的所有用户
for (User user : userList) {
if (!user.equals(sender)) {
user.receiveMessage(message, sender);
}
}
} else {
throw new RuntimeException("发送者未加入聊天");
}
}
}
// 客户端使用
public class MediatorClient {
public static void main(String[] args) {
// 创建中介者
ChatMediator mediator = new ChatMediatorImpl();
// 创建用户(自动注册到中介者)
User user1 = new User("张三", mediator);
User user2 = new User("李四", mediator);
User user3 = new User("王五", mediator);
// 一对一聊天
System.out.println("\n--- 一对一聊天 ---");
user1.sendTo("你好,李四", user2);
// 群聊
System.out.println("\n--- 群聊 ---");
user3.sendGroup("大家好,我是王五");
}
}
优缺点&场景:优点是减少对象间耦合、集中管理交互逻辑、扩展灵活;缺点是中介者可能成为全能类,逻辑复杂。适合纯Java中多对象交互频繁、耦合紧密的场景(聊天系统、组件通信、分布式服务协调)。
10. 解释器模式
核心逻辑:定义一种语言的语法规则,构建解释器来解释该语言中的表达式,将语法解析与执行分离,支持自定义语法规则的解析与执行。
暂时无法在豆包文档外展示此内容
Java实战案例:后端简单运算表达式解析场景,支持“加法+减法”运算语法,通过解释器解析表达式字符串(如“10+20-5”),计算结果,构建表达式树处理复杂语法。
// 上下文:存储表达式中的变量/值,传递解析状态
public class ExpressionContext {
private Map<String, Integer> valueMap = new HashMap<>();
// 设置变量值(如 a=10, b=20)
public void setValue(String key, int value) {
valueMap.put(key, value);
}
// 获取变量值
public int getValue(String key) {
return valueMap.get(key);
}
}
// 抽象表达式
public abstract class AbstractExpression {
public abstract int interpret(ExpressionContext context);
}
// 终结符表达式:变量表达式(解析变量值)
public class VariableExpression extends AbstractExpression {
private String variable;
public VariableExpression(String variable) {
this.variable = variable;
}
@Override
public int interpret(ExpressionContext context) {
return context.getValue(variable); // 从上下文获取变量值
}
}
// 终结符表达式:常量表达式(解析常量值)
public class ConstantExpression extends AbstractExpression {
private int value;
public ConstantExpression(int value) {
this.value = value;
}
@Override
public int interpret(ExpressionContext context) {
return value; // 常量值直接返回
}
}
// 非终结符表达式:加法表达式
public class AddExpression extends AbstractExpression {
private AbstractExpression left; // 左表达式
private AbstractExpression right; // 右表达式
public AddExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(ExpressionContext context) {
// 解释左右表达式并相加
return left.interpret(context) + right.interpret(context);
}
}
// 非终结符表达式:减法表达式
public class SubtractExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public SubtractExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(ExpressionContext context) {
return left.interpret(context) - right.interpret(context);
}
}
// 客户端使用:构建表达式树并解析
public class InterpreterClient {
public static void main(String[] args) {
// 构建表达式:a + b - 5(a=10, b=20)
ExpressionContext context = new ExpressionContext();
context.setValue("a", 10);
context.setValue("b", 20);
// 构建表达式树
AbstractExpression a = new VariableExpression("a");
AbstractExpression b = new VariableExpression("b");
AbstractExpression constant = new ConstantExpression(5);
AbstractExpression add = new AddExpression(a, b);
AbstractExpression subtract = new SubtractExpression(add, constant);
// 解析表达式并计算结果
int result = subtract.interpret(context);
System.out.println("表达式 a + b - 5 的结果:" + result);
}
}
优缺点&场景:优点是语法规则可扩展、解析逻辑清晰;缺点是语法复杂时表达式树庞大,效率低、维护难。适合纯Java中简单语法解析、自定义规则场景(表达式计算、配置解析、简单脚本解释)。
11. 访问者模式
核心逻辑:定义一个访问者对象,封装对元素对象的操作,元素对象接受访问者访问,使操作逻辑与元素本身分离,支持动态扩展对元素的操作。
暂时无法在豆包文档外展示此内容
Java实战案例:后端数据统计场景,元素为“订单”“商品”,访问者为“金额统计访问者”“数量统计访问者”,通过访问者动态扩展统计逻辑,不修改元素类。
// 访问者接口
public interface DataVisitor {
void visit(OrderElement order); // 访问订单元素
void visit(ProductElement product); // 访问商品元素
}
// 元素接口
public interface DataElement {
void accept(DataVisitor visitor); // 接受访问者访问
}
// 具体元素1:订单元素
public class OrderElement implements DataElement {
private String orderNo;
private double amount; // 订单金额
public OrderElement(String orderNo, double amount) {
this.orderNo = orderNo;
this.amount = amount;
}
@Override
public void accept(DataVisitor visitor) {
visitor.visit(this); // 接受访问者,调用对应访问方法
}
// getter
public String getOrderNo() {
return orderNo;
}
public double getAmount() {
return amount;
}
}
// 具体元素2:商品元素
public class ProductElement implements DataElement {
private String productId;
private int count; // 商品数量
public ProductElement(String productId, int count) {
this.productId = productId;
this.count = count;
}
@Override
public void accept(DataVisitor visitor) {
visitor.visit(this);
}
// getter
public String getProductId() {
return productId;
}
public int getCount() {
return count;
}
}
// 具体访问者1:金额统计访问者
public class AmountStatVisitor implements DataVisitor {
private double totalAmount = 0;
@Override
public void visit(OrderElement order) {
totalAmount += order.getAmount(); // 累加订单金额
}
@Override
public void visit(ProductElement product) {
// 商品元素不参与金额统计,空实现
}
// 获取总金额
public double getTotalAmount() {
return totalAmount;
}
}
// 具体访问者2:数量统计访问者
public class CountStatVisitor implements DataVisitor {
private int totalCount = 0;
@Override
public void visit(OrderElement order) {
// 订单元素不参与数量统计,空实现
}
@Override
public void visit(ProductElement product) {
totalCount += product.getCount(); // 累加商品数量
}
// 获取总数量
public int getTotalCount() {
return totalCount;
}
}
// 对象结构:管理元素集合
public class DataStructure {
private List<DataElement> elements = new ArrayList<>();
// 添加元素
public void addElement(DataElement element) {
elements.add(element);
}
// 遍历所有元素,接受访问者访问
public void acceptVisitor(DataVisitor visitor) {
for (DataElement element : elements) {
element.accept(visitor);
}
}
}
// 客户端使用
public class VisitorClient {
public static void main(String[] args) {
// 初始化对象结构并添加元素
DataStructure dataStructure = new DataStructure();
dataStructure.addElement(new OrderElement("ORDER006", 299.9));
dataStructure.addElement(new OrderElement("ORDER007", 599.9));
dataStructure.addElement(new ProductElement("PROD001", 10));
dataStructure.addElement(new ProductElement("PROD002", 20));
// 金额统计
AmountStatVisitor amountVisitor = new AmountStatVisitor();
dataStructure.acceptVisitor(amountVisitor);
System.out.println("订单总金额:" + amountVisitor.getTotalAmount() + "元");
// 数量统计
CountStatVisitor countVisitor = new CountStatVisitor();
dataStructure.acceptVisitor(countVisitor);
System.out.println("商品总数量:" + countVisitor.getTotalCount() + "件");
}
}
优缺点&场景:优点是分离操作与元素、扩展操作灵活、集中管理操作逻辑;缺点是元素类扩展困难(新增元素需修改访问者接口)。适合纯Java中元素稳定、操作多变的场景(数据统计、报表生成、复杂对象遍历操作)。
四、设计模式核心总结
23种设计模式按三大类分工明确:创建型聚焦“对象创建”,结构型聚焦“系统结构”,行为型聚焦“交互行为”,核心目标均为解耦、复用、可维护。实际开发中无需刻意套用,需结合场景选择——简单场景用工厂、单例,复杂对象用建造者、原型,多对象交互用观察者、中介者,状态多变用状态模式,操作扩展用访问者模式。面试中需掌握每种模式的核心逻辑、案例场景、优缺点,结合项目经验说明落地方式,重点理解“组合优于继承”“面向接口编程”等设计原则。