Java 设计模式全景解析
前言:设计模式的本质与价值
设计模式是软件开发中反复出现的问题的成熟解决方案,由 GoF(Gang of Four,四人组:Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)在 1994 年的《Design Patterns: Elements of Reusable Object-Oriented Software》中首次系统提出,共 23 种经典模式。
设计模式的核心价值
- 代码复用:避免重复造轮子,将成熟方案移植到新场景。
- 降低耦合:解耦对象间的依赖关系,提升代码灵活性。
- 提高可读性:统一设计语言,让团队快速理解代码逻辑。
- 应对变化:符合 “开闭原则”(对扩展开放、对修改关闭),降低需求变更的维护成本。
- 标准化设计:将 “经验” 转化为 “方法论”,减少设计失误。
设计模式的分类
根据解决的问题场景,23 种经典模式分为三大类,另有 “并发型”“架构型” 等扩展分类:
第一部分:创建型模式(Creational Patterns)
创建型模式专注于对象创建的过程,通过隐藏对象创建的细节(如实例化逻辑、依赖注入),让用户无需关注 “如何创建”,只需关注 “如何使用”。
1. 单例模式(Singleton Pattern)
1.1 定义
确保一个类仅有一个实例,并提供一个全局访问点。
1.2 核心意图
解决 “一个类多次实例化导致的资源浪费或状态不一致” 问题(如线程池、配置管理器、日志对象)。
1.3 关键结构
- 单例类:私有构造器(禁止外部 new)、静态私有实例、静态公有访问方法。
1.4 实现方式(7 种,按安全性 / 效率排序)
1.4.1 饿汉式(立即加载)
原理:类加载时直接初始化实例,依赖 JVM 类加载的 “线程安全性”(类初始化阶段是线程安全的)。
public class HungrySingleton {// 1. 私有静态实例(类加载时初始化)private static final HungrySingleton INSTANCE = new HungrySingleton();// 2. 私有构造器(禁止外部实例化)private HungrySingleton() {}// 3. 公有静态访问方法public static HungrySingleton getInstance() {return INSTANCE;}
}
- 优点:简单、线程安全(JVM 保证);
- 缺点:类加载时就创建实例,即使不使用也占用内存(如单例对象庞大时浪费资源)。
1.4.2 懒汉式(延迟加载,非线程安全)
原理:第一次调用getInstance()时才创建实例,实现 “延迟加载”。
public class LazySingletonUnsafe {private static LazySingletonUnsafe INSTANCE;private LazySingletonUnsafe() {}public static LazySingletonUnsafe getInstance() {// 线程不安全:多线程同时进入if,会创建多个实例if (INSTANCE == null) {INSTANCE = new LazySingletonUnsafe();}return INSTANCE;}
}
- 优点:延迟加载,节省内存;
- 缺点:多线程环境下存在 “实例重复创建” 问题,生产环境禁用。
1.4.3 懒汉式(线程安全,synchronized 方法)
原理:给getInstance()加synchronized锁,强制单线程执行创建逻辑。
public class LazySingletonSafe1 {private static LazySingletonSafe1 INSTANCE;private LazySingletonSafe1() {}// 加锁保证线程安全public synchronized static LazySingletonSafe1 getInstance() {if (INSTANCE == null) {INSTANCE = new LazySingletonSafe1();}return INSTANCE;}
}
- 优点:线程安全、延迟加载;
- 缺点:锁粒度太大,每次调用getInstance()都要加锁,并发效率极低。
1.4.4 双重检查锁(DCL,Double-Checked Locking)
原理:“两次判空 + volatile 关键字”,既保证线程安全,又减少锁竞争。
public class DCLSingleton {// 必须加volatile:禁止指令重排(避免"半初始化实例"问题)private static volatile DCLSingleton INSTANCE;private DCLSingleton() {}public static DCLSingleton getInstance() {// 第一次判空:避免非空时加锁(提升效率)if (INSTANCE == null) {synchronized (DCLSingleton.class) {// 第二次判空:防止多线程同时进入外层if后,重复创建实例if (INSTANCE == null) {INSTANCE = new DCLSingleton(); // 不加volatile可能发生指令重排:分配内存→实例化→赋值,重排后可能先赋值再实例化// 导致其他线程拿到"半初始化的实例",调用方法时抛出异常}}}return INSTANCE;}
}
- 优点:线程安全、延迟加载、并发效率高;
- 注意:INSTANCE必须加volatile,否则在 JDK 1.5 前存在指令重排风险(JDK 1.5 后volatile禁止重排)。
1.4.5 静态内部类(Holder 模式)
原理:利用 “静态内部类的加载时机”(仅在被首次引用时加载)实现延迟加载,依赖 JVM 类加载的线程安全性。
public class HolderSingleton {// 1. 私有构造器private HolderSingleton() {}// 2. 静态内部类(仅在getInstance()调用时加载)private static class Holder {private static final HolderSingleton INSTANCE = new HolderSingleton();}// 3. 公有访问方法public static HolderSingleton getInstance() {return Holder.INSTANCE;}
}
- 优点:线程安全、延迟加载、无 volatile 依赖,是生产环境推荐方案;
- 缺点:无法防止 “反射破坏单例”(可在构造器中加判断,重复创建时抛异常)。
1.4.6 枚举单例(Enum Singleton)
原理:利用 Java 枚举的特性:枚举类的实例是天然单例(JVM 保证),且无法被反射或序列化破坏。
public enum EnumSingleton {// 唯一实例(枚举常量)INSTANCE;// 枚举类可以有成员方法public void doSomething() {System.out.println("枚举单例执行方法");}
}// 使用方式
EnumSingleton.INSTANCE.doSomething();
-
优点:
- 线程安全(JVM 保证);
- 防止反射破坏(反射无法创建枚举实例);
- 防止序列化破坏(枚举的readObject()方法会返回原有实例);
-
缺点:枚举类加载时实例化,无法延迟加载(但枚举实例通常较小,影响可忽略),是最安全的单例方案。
1.4.7 容器式单例(适用于多实例管理)
原理:用一个 Map 存储多个单例实例,按 key 获取,适合需要管理 “一组单例” 的场景(如 Spring 的 IOC 容器)。
public class ContainerSingleton {private static Map<String, Object> singletonMap = new ConcurrentHashMap<>();private ContainerSingleton() {}// 注册单例public static void registerSingleton(String key, Object instance) {if (!singletonMap.containsKey(key)) {singletonMap.put(key, instance);}}// 获取单例public static Object getSingleton(String key) {return singletonMap.get(key);}
}// 使用
ContainerSingleton.registerSingleton("config", new Config());
Config config = (Config) ContainerSingleton.getSingleton("config");
1.5 应用场景
- 资源密集型对象:线程池、数据库连接池、Redis 客户端;
- 全局状态对象:配置管理器(如ConfigSingleton)、日志对象(如LoggerSingleton);
- 框架中的核心组件:Spring 的ApplicationContext(默认单例)、MyBatis 的SqlSessionFactory。
1.6 常见问题
反射破坏:通过Constructor.setAccessible(true)调用私有构造器,可在构造器中加判断:
private HolderSingleton() {if (Holder.INSTANCE != null) {throw new IllegalStateException("单例类禁止重复实例化");}
}
序列化破坏:普通单例序列化后反序列化会生成新实例,可重写readResolve()方法:
private Object readResolve() {return Holder.INSTANCE;
}
2. 工厂模式(Factory Pattern)
工厂模式分为简单工厂(非 GoF 标准模式)、工厂方法、抽象工厂,核心是 “用工厂类替代new关键字”,解耦 “对象创建” 与 “业务逻辑”。
2.1 简单工厂模式(Simple Factory)
2.1.1 定义
由一个工厂类根据传入的参数,动态创建不同产品类的实例。
2.1.2 结构
- 产品接口:定义产品的共同方法;
- 具体产品:实现产品接口的类;
- 工厂类:包含创建产品的静态方法(根据参数判断创建哪种产品)
2.1.3 实现(以 “支付方式创建” 为例)
// 1. 产品接口
public interface Payment {void pay(double amount);
}// 2. 具体产品:支付宝
public class Alipay implements Payment {@Overridepublic void pay(double amount) {System.out.println("支付宝支付:" + amount + "元");}
}// 3. 具体产品:微信支付
public class WechatPay implements Payment {@Overridepublic void pay(double amount) {System.out.println("微信支付:" + amount + "元");}
}// 4. 工厂类
public class PaymentFactory {// 静态工厂方法:根据参数创建产品public static Payment createPayment(String type) {switch (type) {case "alipay":return new Alipay();case "wechat":return new WechatPay();default:throw new IllegalArgumentException("不支持的支付方式");}}
}// 使用
Payment alipay = PaymentFactory.createPayment("alipay");
alipay.pay(100); // 输出:支付宝支付:100元
2.1.4 优缺点
- 优点:简化对象创建,用户无需知道产品类名,只需知道参数;
- 缺点:违反 “开闭原则”—— 新增产品(如银联支付)需修改工厂类的switch逻辑,工厂类会越来越庞大。
2.2 工厂方法模式(Factory Method)
2.2.1 定义
定义一个创建产品的接口,但让实现该接口的子类决定创建哪种产品。工厂方法将产品的创建延迟到子类。
2.2.2 核心改进
解决简单工厂的 “开闭原则” 问题:新增产品时,只需新增 “具体产品” 和 “具体工厂”,无需修改原有代码。
2.2.3 结构
- 产品接口:定义产品共性;
- 具体产品:实现产品接口;
- 工厂接口:定义创建产品的方法(返回产品接口);
- 具体工厂:实现工厂接口,创建对应的具体产品。
2.2.4 实现(延续支付案例)
// 1. 产品接口(同简单工厂)
public interface Payment {void pay(double amount);
}// 2. 具体产品(同简单工厂:Alipay、WechatPay)// 3. 工厂接口
public interface PaymentFactory {Payment createPayment(); // 工厂方法:返回产品接口
}// 4. 具体工厂:支付宝工厂
public class AlipayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {return new Alipay();}
}// 5. 具体工厂:微信支付工厂
public class WechatPayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {return new WechatPay();}
}// 6. 新增产品:银联支付
public class UnionPay implements Payment {@Overridepublic void pay(double amount) {System.out.println("银联支付:" + amount + "元");}
}// 7. 新增工厂:银联支付工厂(无需修改原有代码)
public class UnionPayFactory implements PaymentFactory {@Overridepublic Payment createPayment() {return new UnionPay();}
}// 使用
PaymentFactory factory = new AlipayFactory();
Payment alipay = factory.createPayment();
alipay.pay(100);
2.2.5 优缺点
- 优点:符合开闭原则、解耦程度更高、扩展性强;
- 缺点:新增产品需同时新增工厂类,导致类数量增多(“类爆炸”)。
2.3 抽象工厂模式(Abstract Factory)
2.3.1 定义
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2.3.2 核心场景
当产品是 “产品族”(多个相关产品组成的集合)时,工厂方法无法满足需求。例如:
- 操作系统产品族:Windows 系统的 “按钮 + 文本框”、Mac 系统的 “按钮 + 文本框”;
- 支付产品族:支付宝的 “支付 + 退款”、微信支付的 “支付 + 退款”。
2.3.3 关键概念
- 产品族:同一品牌下的相关产品(如 “支付宝支付 + 支付宝退款”);
- 产品等级结构:同一类型的不同品牌产品(如 “支付宝支付、微信支付” 都属于 “支付” 等级)。
2.3.4 结构
- 抽象产品接口:按产品等级定义(如Payment、Refund);
- 具体产品:实现抽象产品接口(如AlipayPayment、AlipayRefund);
- 抽象工厂接口:定义创建 “产品族” 中所有产品的方法(如createPayment()、createRefund());
- 具体工厂:实现抽象工厂接口,创建对应品牌的产品族(如AlipayFactory创建支付宝的支付和退款)。
2.3.5 实现(支付 + 退款产品族)
// 1. 抽象产品1:支付
public interface Payment {void pay(double amount);
}// 2. 抽象产品2:退款
public interface Refund {void refund(double amount);
}// 3. 具体产品:支付宝支付
public class AlipayPayment implements Payment {@Overridepublic void pay(double amount) {System.out.println("支付宝支付:" + amount + "元");}
}// 4. 具体产品:支付宝退款
public class AlipayRefund implements Refund {@Overridepublic void refund(double amount) {System.out.println("支付宝退款:" + amount + "元");}
}// 5. 具体产品:微信支付
public class WechatPayment implements Payment {@Overridepublic void pay(double amount) {System.out.println("微信支付:" + amount + "元");}
}// 6. 具体产品:微信退款
public class WechatRefund implements Refund {@Overridepublic void refund(double amount) {System.out.println("微信退款:" + amount + "元");}
}// 7. 抽象工厂接口(定义产品族的创建方法)
public interface PayPlatformFactory {Payment createPayment(); // 创建支付产品Refund createRefund(); // 创建退款产品
}// 8. 具体工厂:支付宝工厂(创建支付宝产品族)
public class AlipayFactory implements PayPlatformFactory {@Overridepublic Payment createPayment() {return new AlipayPayment();}@Overridepublic Refund createRefund() {return new AlipayRefund();}
}// 9. 具体工厂:微信支付工厂(创建微信产品族)
public class WechatPayFactory implements PayPlatformFactory {@Overridepublic Payment createPayment() {return new WechatPayment();}@Overridepublic Refund createRefund() {return new WechatRefund();}
}// 使用
PayPlatformFactory alipayFactory = new AlipayFactory();
Payment alipayPay = alipayFactory.createPayment();
Refund alipayRefund = alipayFactory.createRefund();
alipayPay.pay(200); // 支付宝支付:200元
alipayRefund.refund(50); // 支付宝退款:50元
2.3.6 优缺点
- 优点:
- 保证产品族内的产品一致性(如支付宝工厂不会创建微信的退款);
- 符合开闭原则,新增产品族只需新增具体工厂;
- 缺点:
- 新增产品等级(如新增 “对账” 功能)需修改抽象工厂和所有具体工厂,违反开闭原则;
- 类结构复杂,理解成本高。
2.4 工厂模式对比与应用
3. 建造者模式(Builder Pattern)
3.1 定义
将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。
3.2 核心意图
解决 “复杂对象创建时参数过多、构造器臃肿” 问题。例如:创建一个User对象,包含姓名、年龄、手机号、邮箱、地址等 10 个字段,若用构造器需重载多个版本(如User(name, age)、User(name, age, phone)),且参数顺序易混淆。
3.3 结构
- 产品:复杂对象(如User);
- 建造者接口:定义构建产品各个部件的方法(如setName()、setAge());
- 具体建造者:实现建造者接口,完成产品的构建;
- 指挥者(可选):负责调用建造者的方法,控制构建流程(简化用户调用)。
3.4 实现(以 “User 对象构建” 为例)
3.4.1 基础实现(无指挥者,链式调用)
// 1. 产品:复杂对象User
public class User {private String name; // 必选private int age; // 可选private String phone; // 可选private String email; // 可选// 私有构造器:仅允许建造者创建private User(UserBuilder builder) {this.name = builder.name;this.age = builder.age;this.phone = builder.phone;this.email = builder.email;}// 2. 建造者(静态内部类,方便访问User的私有成员)public static class UserBuilder {private String name; // 必选参数(在构造器中要求)private int age; // 可选参数(默认值)private String phone;private String email;// 必选参数通过建造者的构造器传入public UserBuilder(String name) {this.name = name;}// 可选参数:链式调用(返回this)public UserBuilder age(int age) {this.age = age;return this;}public UserBuilder phone(String phone) {this.phone = phone;return this;}public UserBuilder email(String email) {this.email = email;return this;}// 构建产品public User build() {// 可在这里加参数校验(如name不为空)if (name == null || name.isEmpty()) {throw new IllegalArgumentException("姓名不能为空");}return new User(this);}}// getter方法(User通常是不可变对象,无setter)public String getName() { return name; }public int getAge() { return age; }public String getPhone() { return phone; }public String getEmail() { return email; }
}// 使用:链式调用,清晰易懂
User user = new User.UserBuilder("张三").age(25).phone("13800138000").email("zhangsan@example.com").build();
3.4.2 带指挥者的实现(复杂流程控制)
当构建流程复杂时(如 “必须先设置姓名,再设置手机号,最后设置邮箱”),可加指挥者:
// 指挥者:控制构建流程
public class UserDirector {private UserBuilder builder;public UserDirector(UserBuilder builder) {this.builder = builder;}// 固定构建流程public User buildDefaultUser(String name) {return builder.name(name).age(18) // 默认年龄18.phone("10086") // 默认手机号.build();}
}// 使用指挥者
UserBuilder builder = new User.UserBuilder("李四");
UserDirector director = new UserDirector(builder);
User defaultUser = director.buildDefaultUser("李四");
3.5 优缺点
- 优点:
- 解耦对象构建与表示,参数设置灵活;
- 链式调用清晰,避免构造器参数臃肿;
- 可在build()方法中加参数校验;
- 缺点:
- 新增产品需新增建造者,类数量增多;
- 建造者与产品强耦合(建造者需知道产品的所有字段)。
3.6 应用场景
- 复杂对象创建:如User、Order、Report等包含多个可选字段的对象;
- 框架中的应用:
- Lombok 的@Builder注解(自动生成建造者);
- JDK 的StringBuilder(本质是建造者,构建字符串);
- MyBatis 的SqlSessionFactoryBuilder(构建SqlSessionFactory)。
4. 原型模式(Prototype Pattern)
4.1 定义
用一个已有的实例作为原型,通过复制(克隆) 该原型来创建新的对象,而无需重新执行复杂的初始化逻辑。
4.2 核心意图
解决 “对象创建成本高(如初始化需查询数据库、网络请求)” 的问题。例如:创建一个User对象需要查询 3 张表,若需创建 10 个相似的User,可克隆原型而非重复查询。
4.3 核心概念
- 浅克隆:复制对象时,仅复制基本数据类型字段,引用类型字段仍指向原对象(共享引用);
- 深克隆:复制对象时,基本数据类型和引用类型字段都复制(完全独立)。
4.4 实现(基于 Java 的Cloneable接口)
// 产品:实现Cloneable接口(标记接口,无实际方法)
public class User implements Cloneable {private String name; // 基本数据类型(String是不可变对象,特殊)private Address address;// 引用类型(自定义对象)public User(String name, Address address) {this.name = name;this.address = address;}// 重写clone()方法(JVM提供克隆实现)@Overrideprotected User clone() throws CloneNotSupportedException {return (User) super.clone();}// getter/setterpublic String getName() { return name; }public void setName(String name) { this.name = name; }public Address getAddress() { return address; }public void setAddress(Address address) { this.address = address; }
}// 引用类型:地址
public class Address {private String city;public Address(String city) {this.city = city;}// getter/setterpublic String getCity() { return city; }public void setCity(String city) { this.city = city; }
}// 测试浅克隆
public class PrototypeTest {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("北京");User prototype = new User("张三", address);// 克隆原型User cloneUser = prototype.clone();// 1. 基本数据类型:独立cloneUser.setName("李四");System.out.println(prototype.getName()); // 张三(原对象不变)// 2. 引用类型:共享cloneUser.getAddress().setCity("上海");System.out.println(prototype.getAddress().getCity()); // 上海(原对象的address被修改)}
}
4.4.2 深克隆(两种方式)
方式 1:递归克隆引用类型
public class User implements Cloneable {private String name;private Address address;@Overrideprotected User clone() throws CloneNotSupportedException {// 1. 克隆User本身(浅克隆)User cloneUser = (User) super.clone();// 2. 克隆引用类型字段(递归克隆)if (address != null) {cloneUser.address = (Address) address.clone();}return cloneUser;}
}// Address也需实现Cloneable
public class Address implements Cloneable {private String city;@Overrideprotected Address clone() throws CloneNotSupportedException {return (Address) super.clone();}
}// 测试:引用类型独立
cloneUser.getAddress().setCity("上海");
System.out.println(prototype.getAddress().getCity()); // 北京(原对象不变)
方式 2:序列化与反序列化(推荐,无需递归)
// 产品和引用类型都需实现Serializable接口
public class User implements Serializable {private String name;private Address address;// 深克隆方法public User deepClone() throws IOException, ClassNotFoundException {// 1. 序列化:将对象写入字节流ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);// 2. 反序列化:从字节流读取对象(新实例)ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (User) ois.readObject();}
}public class Address implements Serializable {private String city;
}// 使用
User cloneUser = prototype.deepClone();
4.5 优缺点
- 优点:
- 避免重复初始化,提高对象创建效率;
- 简化复杂对象的创建(克隆比 new+setter 更简单);
- 缺点:
- 深克隆实现复杂(需处理所有引用类型);
- 序列化方式克隆时,对象必须实现Serializable( transient 字段无法克隆)。
4.6 应用场景
- 频繁创建相似对象:如游戏中的敌人实例、Excel 中的行对象;
- 对象创建成本高:如初始化需查询数据库、调用远程接口;
- 框架中的应用:
- Spring 的BeanUtils.copyProperties()(基于反射的浅克隆);
- Redis 的 RDB 持久化(克隆内存数据到磁盘,避免阻塞主线程)。
第二部分:结构型模式(Structural Patterns)
结构型模式专注于对象或类的组合方式,通过优化类的继承关系、对象的关联关系,实现 “低耦合、高复用” 的代码结构。
1. 适配器模式(Adapter Pattern)
1.1 定义
将一个类的接口转换成客户期望的另一个接口,使得原本因接口不兼容而无法一起工作的类可以协同工作。
1.2 核心意图
解决 “接口不匹配” 问题,类似生活中的 “电源适配器”(220V 转 5V)、“USB 转 Type-C 转接器”。
1.3 两种实现方式
根据适配器与适配者的关系,分为类适配器(继承)和对象适配器(组合)。
1.4 类适配器模式(基于继承)
1.4.1 结构
- 目标接口(Target):客户期望的接口;
- 适配者(Adaptee):已存在的、接口不兼容的类;
- 适配器(Adapter):继承适配者,实现目标接口,将适配者的方法转换为目标接口的方法。
1.4.2 实现(以 “旧系统接口适配新系统” 为例)
场景:旧系统有一个OldCalculator(只能计算整数加法),新系统期望NewCalculator接口(能计算小数加法)。
// 1. 目标接口:新系统期望的接口
public interface NewCalculator {double add(double a, double b);
}// 2. 适配者:旧系统的类(接口不兼容)
public class OldCalculator {// 只能计算整数加法public int addInt(int a, int b) {return a + b;}
}// 3. 适配器:继承适配者,实现目标接口
public class CalculatorAdapter extends OldCalculator implements NewCalculator {@Overridepublic double add(double a, double b) {// 将小数转换为整数(适配逻辑),调用适配者的方法int intA = (int) a;int intB = (int) b;return super.addInt(intA, intB);}
}// 使用
NewCalculator calculator = new CalculatorAdapter();
System.out.println(calculator.add(1.5, 2.8)); // 3.0(适配后结果)
1.5 对象适配器模式(基于组合,推荐)
1.5.1 结构
适配器不继承适配者,而是持有适配者的引用,通过组合实现适配(更符合 “合成复用原则”:优先组合,少用继承)。
1.5.2 实现(同上场景)
// 1. 目标接口(同前)
public interface NewCalculator {double add(double a, double b);
}// 2. 适配者(同前)
public class OldCalculator {public int addInt(int a, int b) {return a + b;}
}// 3. 适配器:持有适配者引用(组合)
public class CalculatorAdapter implements NewCalculator {private OldCalculator oldCalculator; // 适配者引用// 通过构造器注入适配者public CalculatorAdapter(OldCalculator oldCalculator) {this.oldCalculator = oldCalculator;}@Overridepublic double add(double a, double b) {int intA = (int) a;int intB = (int) b;return oldCalculator.addInt(intA, intB); // 调用适配者方法}
}// 使用
OldCalculator oldCalc = new OldCalculator();
NewCalculator calculator = new CalculatorAdapter(oldCalc);
System.out.println(calculator.add(1.5, 2.8)); // 3.0
1.6 两种适配器对比
1.7 应用场景
- 旧系统改造:新系统需调用旧系统接口,接口不兼容;
- 第三方库集成:引入的第三方库接口与本地系统不匹配;
- 框架中的应用:
- JDK 的InputStreamReader(将InputStream(字节流)适配为Reader(字符流));
- Spring 的HandlerAdapter(适配不同的处理器Handler,统一返回ModelAndView);
- MyBatis 的TypeHandler(将数据库类型适配为 Java 类型)。
2. 装饰器模式(Decorator Pattern)
2.1 定义
动态地给一个对象添加额外的职责,就像给房子刷漆、贴墙纸一样,不改变房子本身的结构。
2.2 核心意图
解决 “通过继承扩展功能导致的类爆炸” 问题。例如:给咖啡加奶、加糖、加摩卡,若用继承需创建MilkCoffee、SugarCoffee、MilkSugarCoffee等无数类,而装饰器模式可动态组合。
2.3 结构
- 抽象组件(Component):定义被装饰对象的接口(如Coffee);
- 具体组件(ConcreteComponent):实现抽象组件的基本对象(如SimpleCoffee);
- 抽象装饰器(Decorator):持有抽象组件的引用,实现抽象组件接口(统一装饰接口);
- 具体装饰器(ConcreteDecorator):实现抽象装饰器,添加额外职责(如MilkDecorator、SugarDecorator)。
2.4 实现(咖啡加配料场景)
// 1. 抽象组件:咖啡接口
public interface Coffee {String getDescription(); // 描述double getPrice(); // 价格
}// 2. 具体组件:基础咖啡(无配料)
public class SimpleCoffee implements Coffee {@Overridepublic String getDescription() {return "基础咖啡";}@Overridepublic double getPrice() {return 10.0;}
}// 3. 抽象装饰器:咖啡装饰器(持有咖啡引用)
public abstract class CoffeeDecorator implements Coffee {protected Coffee coffee; // 被装饰的咖啡// 构造器注入被装饰对象public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}
}// 4. 具体装饰器1:加奶
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription() + " + 牛奶"; // 追加描述}@Overridepublic double getPrice() {return coffee.getPrice() + 2.0; // 追加价格}
}// 5. 具体装饰器2:加糖
public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic String getDescription() {return coffee.getDescription() + " + 糖";}@Overridepublic double getPrice() {return coffee.getPrice() + 1.0;}
}// 使用:动态组合装饰器
public class DecoratorTest {public static void main(String[] args) {// 基础咖啡Coffee coffee = new SimpleCoffee();System.out.println(coffee.getDescription() + ":" + coffee.getPrice() + "元"); // 基础咖啡:10.0元// 加奶coffee = new MilkDecorator(coffee);System.out.println(coffee.getDescription() + ":" + coffee.getPrice() + "元"); // 基础咖啡 + 牛奶:12.0元// 再加糖coffee = new SugarDecorator(coffee);System.out.println(coffee.getDescription() + ":" + coffee.getPrice() + "元"); // 基础咖啡 + 牛奶 + 糖:13.0元}
}
2.5 优缺点
- 优点:
- 灵活扩展:无需继承,动态给对象添加功能,可组合多个装饰器;
- 单一职责:每个装饰器只负责一个额外功能(如加奶、加糖);
- 缺点:
- 类数量增多:每个功能需一个装饰器;
- 调试复杂:对象被多层装饰,调用链长。
2.6 应用场景
- 动态扩展对象功能:如日志增强、性能监控、权限校验;
- 避免继承导致的类爆炸:如 IO 流、GUI 组件;
- 框架中的应用:
- JDK 的 IO 流(InputStream是抽象组件,FileInputStream是具体组件, BufferedInputStream、DataInputStream是装饰器);
- Spring 的TransactionAwareCacheDecorator(给缓存添加事务支持);
- MyBatis 的CacheDecoratorChain(给缓存添加日志、序列化等功能)。
3. 代理模式(Proxy Pattern)
3.1 定义
为其他对象提供一种代理以控制对这个对象的访问。代理对象充当 “中间人”,可以在目标对象的方法执行前后添加额外逻辑。
3.2 核心意图
解决 “直接访问目标对象导致的耦合过高或功能缺失” 问题,如:
- 远程访问:目标对象在另一台服务器,需代理处理网络通信;
- 权限控制:访问目标对象前先校验权限;
- 性能监控:记录目标方法的执行时间。
3.3 三种代理类型
3.4 静态代理实现(权限控制场景)
// 1. 接口:目标对象和代理对象的共同接口
public interface UserService {void updateUser(String username);
}// 2. 目标对象:真实的业务逻辑
public class UserServiceImpl implements UserService {@Overridepublic void updateUser(String username) {System.out.println("更新用户:" + username);}
}// 3. 代理对象:实现相同接口,持有目标对象引用
public class UserServiceProxy implements UserService {private UserService target; // 目标对象private String role; // 额外参数:角色(用于权限校验)public UserServiceProxy(UserService target, String role) {this.target = target;this.role = role;}@Overridepublic void updateUser(String username) {// 1. 前置增强:权限校验if (!"admin".equals(role)) {System.out.println("权限不足,无法更新用户");return;}// 2. 调用目标对象方法target.updateUser(username);// 3. 后置增强:日志记录System.out.println("更新用户日志:" + username + "更新成功");}
}// 使用
public class ProxyTest {public static void main(String[] args) {// 目标对象UserService target = new UserServiceImpl();// 代理对象(admin角色)UserService adminProxy = new UserServiceProxy(target, "admin");adminProxy.updateUser("张三"); // 输出:权限校验通过 → 更新用户:张三 → 更新用户日志:张三更新成功// 代理对象(普通用户角色)UserService userProxy = new UserServiceProxy(target, "user");userProxy.updateUser("李四"); // 输出:权限不足,无法更新用户}
}
3.5 动态代理实现(JDK 动态代理)
JDK 动态代理基于接口,运行时通过java.lang.reflect.Proxy生成代理类,无需手动创建代理类。
3.5.1 核心组件
- InvocationHandler:代理逻辑处理器,重写invoke()方法(包含前置 / 后置增强逻辑);
- Proxy.newProxyInstance():生成代理对象的静态方法。
3.5.2 实现(通用日志代理)
// 1. 业务接口(同静态代理:UserService)
// 2. 目标对象(同静态代理:UserServiceImpl)// 3. 代理逻辑处理器:实现InvocationHandler
public class LogInvocationHandler implements InvocationHandler {private Object target; // 目标对象(支持任意类型,通用代理)public LogInvocationHandler(Object target) {this.target = target;}// 代理方法:每次调用代理对象的方法,都会执行invoke()@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置增强:日志记录System.out.println("方法" + method.getName() + "开始执行,参数:" + Arrays.toString(args));// 调用目标对象的方法Object result = method.invoke(target, args);// 后置增强:日志记录System.out.println("方法" + method.getName() + "执行结束,返回值:" + result);return result;}
}// 使用:生成动态代理对象
public class DynamicProxyTest {public static void main(String[] args) {// 目标对象UserService target = new UserServiceImpl();// 代理逻辑处理器InvocationHandler handler = new LogInvocationHandler(target);// 生成代理对象(参数:类加载器、目标接口、处理器)UserService proxy = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);// 调用代理对象方法proxy.updateUser("张三");// 输出:// 方法updateUser开始执行,参数:[张三]// 更新用户:张三// 方法updateUser执行结束,返回值:null}
}
3.6 CGLIB 动态代理(基于子类)
JDK 动态代理要求目标对象必须实现接口,若目标对象是无接口的类,需用 CGLIB(Code Generation Library)动态代理:通过生成目标对象的子类作为代理对象。
3.6.1 实现(需引入 CGLIB 依赖)
<!-- Maven依赖 -->
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
// 1. 目标对象:无接口的类
public class UserService {public void updateUser(String username) {System.out.println("更新用户:" + username);}
}// 2. 代理逻辑:实现MethodInterceptor
public class CglibProxyInterceptor implements MethodInterceptor {private Object target;public CglibProxyInterceptor(Object target) {this.target = target;}// 生成代理对象public Object getProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass()); // 设置父类(目标对象)enhancer.setCallback(this); // 设置回调(当前拦截器)return enhancer.create(); // 生成代理对象}// 代理方法:调用代理对象方法时执行@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("CGLIB代理:方法" + method.getName() + "开始执行");Object result = methodProxy.invoke(target, args); // 调用目标方法System.out.println("CGLIB代理:方法" + method.getName() + "执行结束");return result;}
}// 使用
public class CglibProxyTest {public static void main(String[] args) {UserService target = new UserService();CglibProxyInterceptor interceptor = new CglibProxyInterceptor(target);UserService proxy = (UserService) interceptor.getProxy();proxy.updateUser("张三");// 输出:// CGLIB代理:方法updateUser开始执行// 更新用户:张三// CGLIB代理:方法updateUser执行结束}
}
3.7 代理模式应用场景
- 权限控制:如 Spring Security 的方法级权限校验;
- 性能监控:如 APM 工具(SkyWalking)记录方法执行时间;
- 缓存:如 MyBatis 的一级缓存、二级缓存(代理对象拦截查询方法);
- 事务管理:如 Spring 的声明式事务(代理对象在方法前后开启 / 提交事务);
- 远程调用:如 Dubbo 的服务代理(代理对象处理网络通信)。
4. 其他结构型模式(简介与应用)
4.1 桥接模式(Bridge Pattern)
- 定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- 核心场景:解决 “多维度变化导致的类爆炸”。例如:“手机品牌”(华为、小米)和 “手机功能”(打电话、发短信)是两个维度,若用继承需HuaweiCallPhone、XiaomiSmsPhone等类,桥接模式通过 “抽象类持有实现类引用” 解耦。
- 应用:JDK 的JDBC(DriverManager是抽象,不同数据库的Driver是实现,桥接不同数据库)。
4.2 组合模式(Composite Pattern)
- 定义:将对象组合成树形结构以表示 “部分 - 整体” 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
- 核心场景:处理树形结构数据。例如:文件系统(文件是叶子节点,文件夹是组合节点)、UI 组件(按钮是叶子,面板是组合)。
- 应用:JDK 的java.awt.Container(容器组件可包含其他组件,符合组合模式)。
4.3 外观模式(Facade Pattern)
- 定义:为子系统中的一组接口提供一个统一的入口,外观模式定义了一个高层接口,使子系统更易于使用。
- 核心场景:简化复杂子系统的调用。例如:医院的 “分诊台”(统一处理挂号、就诊、缴费,用户无需找各个科室)。
- 应用:Spring 的ApplicationContext(作为外观,统一管理 Bean 的创建、依赖注入等子系统)。
4.4 享元模式(Flyweight Pattern)
- 定义:运用共享技术来有效地支持大量细粒度对象的复用。
- 核心场景:对象数量多且重复(如棋子、字符)。例如:围棋有 361 个位置,若每个位置创建一个Chess对象,共 361 个,但黑白棋子仅 2 种,享元模式共享 “黑棋”“白棋” 两个对象,仅修改位置属性。
- 应用:JDK 的String常量池(相同字符串共享同一对象)、MyBatis 的SqlSession池(共享 SqlSession 对象)。
第三部分:行为型模式(Behavioral Patterns)
行为型模式专注于对象间的交互方式和职责分配,解决 “对象如何协作完成复杂功能” 的问题。
1. 观察者模式(Observer Pattern)
1.1 定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
1.2 核心意图
解决 “对象状态变化时,多个相关对象需同步更新” 的问题,类似 “订阅 - 发布” 模型(如公众号订阅:公众号(被观察者)更新,所有订阅者(观察者)收到通知)。
1.3 结构
- 被观察者(Subject):维护一个观察者列表,提供添加、删除观察者的方法,以及通知所有观察者的方法;
- 观察者(Observer):定义接收通知的接口(如update()方法);
- 具体被观察者(ConcreteSubject):状态变化时调用notifyObservers();
- 具体观察者(ConcreteObserver):实现update()方法,处理通知。
1.4 实现(公众号订阅场景)
// 1. 观察者接口
public interface Observer {void update(String message); // 接收通知的方法
}// 2. 具体观察者:用户A
public class UserA implements Observer {private String name;public UserA(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + "收到消息:" + message);}
}// 3. 具体观察者:用户B
public class UserB implements Observer {private String name;public UserB(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + "收到消息:" + message);}
}// 4. 被观察者接口
public interface Subject {void addObserver(Observer observer); // 添加观察者void removeObserver(Observer observer); // 删除观察者void notifyObservers(String message); // 通知所有观察者
}// 5. 具体被观察者:公众号
public class WechatOfficialAccount implements Subject {private List<Observer> observers = new ArrayList<>(); // 观察者列表@Overridepublic void addObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String message) {// 遍历观察者列表,发送通知for (Observer observer : observers) {observer.update(message);}}// 公众号发布文章(状态变化)public void publishArticle(String title) {System.out.println("公众号发布文章:" + title);notifyObservers("新文章:" + title); // 通知观察者}
}// 使用
public class ObserverTest {public static void main(String[] args) {// 被观察者:公众号WechatOfficialAccount account = new WechatOfficialAccount();// 观察者:用户A、用户BObserver userA = new UserA("张三");Observer userB = new UserB("李四");// 订阅account.addObserver(userA);account.addObserver(userB);// 发布文章account.publishArticle("Java设计模式详解");// 输出:// 公众号发布文章:Java设计模式详解// 张三收到消息:新文章:Java设计模式详解// 李四收到消息:新文章:Java设计模式详解// 取消订阅(用户B)account.removeObserver(userB);account.publishArticle("Spring源码解析");// 输出:// 公众号发布文章:Spring源码解析// 张三收到消息:新文章:Spring源码解析}
}
1.5 推模型与拉模型
- 推模型:被观察者主动将数据推给观察者(如上述示例,直接传递message);
- 拉模型:被观察者仅通知观察者,观察者主动从被观察者拉取数据(更灵活,观察者可按需获取数据)。
1.6 优缺点
- 优点:
- 解耦被观察者与观察者(双方仅依赖接口);
- 扩展性强(新增观察者无需修改被观察者);
- 缺点:
- 观察者过多时,通知效率低;
- 可能导致 “循环依赖”(观察者与被观察者相互依赖)。
1.7 应用场景
- 状态同步:如订单状态变更(支付成功→通知库存、物流、积分系统);
- 事件监听:如 GUI 按钮点击事件(按钮是被观察者,点击后通知所有监听器);
- 框架中的应用:
- JDK 的java.util.Observable和java.util.Observer(内置观察者实现);
- Spring 的ApplicationEvent和ApplicationListener(事件驱动模型,如ContextRefreshedEvent);
- RxJava/RxAndroid(响应式编程框架,基于观察者模式)。
2. 策略模式(Strategy Pattern)
2.1 定义
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
2.2 核心意图
解决 “多种算法(策略)需动态切换” 的问题,避免用if-else或switch判断(如 “支付方式切换”“排序算法切换”)。
2.3 结构
- 策略接口(Strategy):定义算法的共同方法;
- 具体策略(ConcreteStrategy):实现策略接口的具体算法;
- 上下文(Context):持有策略接口的引用,负责调用策略(用户通过上下文使用策略,无需直接接触具体策略)。
2.4 实现(支付方式切换场景)
// 1. 策略接口:支付策略
public interface PaymentStrategy {void pay(double amount);
}// 2. 具体策略1:支付宝支付
public class AlipayStrategy implements PaymentStrategy {private String username;private String password;public AlipayStrategy(String username, String password) {this.username = username;this.password = password;}@Overridepublic void pay(double amount) {System.out.println(username + "使用支付宝支付:" + amount + "元");}
}// 3. 具体策略2:微信支付
public class WechatPayStrategy implements PaymentStrategy {private String openId;public WechatPayStrategy(String openId) {this.openId = openId;}@Overridepublic void pay(double amount) {System.out.println("OpenId:" + openId + "使用微信支付:" + amount + "元");}
}// 4. 上下文:订单(使用策略)
public class Order {private double amount;private PaymentStrategy paymentStrategy; // 策略引用public Order(double amount) {this.amount = amount;}// 设置策略(动态切换)public void setPaymentStrategy(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}// 调用策略的方法public void pay() {paymentStrategy.pay(amount);}
}// 使用
public class StrategyTest {public static void main(String[] args) {// 创建订单(100元)Order order = new Order(100.0);// 选择支付宝支付(切换策略1)order.setPaymentStrategy(new AlipayStrategy("zhangsan", "123456"));order.pay(); // 输出:zhangsan使用支付宝支付:100.0元// 切换为微信支付(切换策略2)order.setPaymentStrategy(new WechatPayStrategy("o6_bmjrPTlm6_2sgVt7hMZOPfL2M"));order.pay(); // 输出:OpenId:o6_bmjrPTlm6_2sgVt7hMZOPfL2M使用微信支付:100.0元}
}
2.5 优缺点
- 优点:
- 避免if-else堆积(新增策略无需修改原有代码);
- 算法可动态切换,灵活性高;
- 缺点:
- 策略类数量增多(每个算法一个类);
- 用户需了解所有策略(才能选择合适的策略)。
2.6 应用场景
- 多种算法可选:如排序算法(冒泡、快排、归并)、加密算法(MD5、SHA-256);
- 动态切换策略:如电商平台的促销策略(满减、折扣、优惠券);
- 框架中的应用:
- JDK 的Comparator接口(排序策略,如Collections.sort(list, comparator));
- Spring 的Resource接口(资源加载策略,如ClassPathResource、FileSystemResource);
- MyBatis 的Executor接口(执行策略,如SIMPLE、REUSE、BATCH)。
3. 模板方法模式(Template Method Pattern)
3.1 定义
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
3.2 核心意图
解决 “多个类的算法流程相同,但部分步骤实现不同” 的问题。例如:“泡茶” 和 “泡咖啡” 的流程都是 “烧开水→加原料→加水→搅拌”,但 “原料” 和 “搅拌方式” 不同。
3.3 结构
- 抽象类(AbstractClass):定义算法骨架(模板方法),包含抽象方法(延迟到子类实现)和具体方法(固定步骤);
- 具体子类(ConcreteClass):实现抽象类中的抽象方法,重定义算法的特定步骤。
3.4 实现(泡茶与泡咖啡场景)
// 1. 抽象类:饮料(定义算法骨架)
public abstract class Beverage {// 模板方法:算法骨架(用final修饰,禁止子类修改流程)public final void makeBeverage() {boilWater(); // 步骤1:烧开水(固定步骤)addIngredient();// 步骤2:加原料(抽象步骤,子类实现)pourWater(); // 步骤3:加水(固定步骤)stir(); // 步骤4:搅拌(钩子方法,可选重写)}// 具体方法:固定步骤private void boilWater() {System.out.println("烧开水");}// 具体方法:固定步骤private void pourWater() {System.out.println("加水");}// 抽象方法:可变步骤(子类必须实现)protected abstract void addIngredient();// 钩子方法(Hook Method):可选重写(默认实现)protected void stir() {System.out.println("默认搅拌");}
}// 2. 具体子类:咖啡
public class Coffee extends Beverage {@Overrideprotected void addIngredient() {System.out.println("加咖啡粉"); // 咖啡的原料}// 重写钩子方法(咖啡需要特殊搅拌)@Overrideprotected void stir() {System.out.println("快速搅拌咖啡");}
}// 3. 具体子类:茶
public class Tea extends Beverage {@Overrideprotected void addIngredient() {System.out.println("加茶叶"); // 茶的原料}// 不重写钩子方法(使用默认搅拌)
}// 使用
public class TemplateMethodTest {public static void main(String[] args) {// 泡咖啡Beverage coffee = new Coffee();System.out.println("泡咖啡:");coffee.makeBeverage();// 输出:// 泡咖啡:// 烧开水// 加咖啡粉// 加水// 快速搅拌咖啡// 泡茶Beverage tea = new Tea();System.out.println("泡茶:");tea.makeBeverage();// 输出:// 泡茶:// 烧开水// 加茶叶// 加水// 默认搅拌}
}
3.5 关键概念:钩子方法
钩子方法是抽象类中定义的可选重写的方法(有默认实现),用于 “控制算法流程” 或 “扩展额外逻辑”。例如:在模板方法中加入判断,根据钩子方法的返回值决定是否执行某个步骤:
// 抽象类中新增钩子方法
protected boolean needSugar() {return false; // 默认不加糖
}// 模板方法中加入判断
public final void makeBeverage() {boilWater();addIngredient();pourWater();if (needSugar()) { // 根据钩子方法判断是否加糖addSugar();}stir();
}// 具体子类重写钩子方法(咖啡需要加糖)
public class Coffee extends Beverage {@Overrideprotected boolean needSugar() {return true;}private void addSugar() {System.out.println("加糖");}
}
3.6 优缺点
- 优点:
- 复用算法骨架(避免代码重复);
- 控制子类扩展(子类仅能修改特定步骤,无法改变流程);
- 缺点:
- 抽象类可能过于庞大(包含多个步骤和钩子);
- 子类数量增多(每个变体一个子类)。
3.7 应用场景
- 算法流程固定、步骤可变:如框架中的生命周期方法(初始化→执行→销毁);
- 代码复用:如 Spring 的AbstractApplicationContext(refresh()方法是模板方法,定义容器初始化流程);
- 框架中的应用:
- JDK 的AbstractList(get()是抽象方法,size()可重写,iterator()是模板方法);
- Spring 的JdbcTemplate(模板方法封装 JDBC 流程,RowMapper是可变步骤);
- MyBatis 的BaseExecutor(query()是模板方法,doQuery()是抽象方法)。
4. 其他行为型模式(简介与应用)
4.1 职责链模式(Chain of Responsibility Pattern)
- 定义:为请求创建一个接收者对象的链,使每个接收者都有机会处理请求,请求沿着链传递,直到有一个接收者处理它为止。
- 核心场景:请求需要多个对象处理,且处理者不确定(如请假审批:组长→部门经理→CEO)。
- 应用:Spring 的HandlerInterceptor(拦截器链,处理请求)、Sentinel 的限流规则链。
4.2 命令模式(Command Pattern)
- 定义:将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
- 核心场景:需要 “记录请求”“撤销操作”“异步执行”(如遥控器按钮→命令→家电,按钮无需知道家电具体操作)。
- 应用:Spring 的JmsTemplate(发送消息封装为命令)、GUI 的撤销 / 重做功能。
4.3 迭代器模式(Iterator Pattern)
- 定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
- 核心场景:遍历集合对象(如 List、Set),隐藏集合的内部结构。
- 应用:JDK 的java.util.Iterator(所有集合的迭代器)、MyBatis 的ResultSetIterator(遍历查询结果)。
4.4 中介者模式(Mediator Pattern)
- 定义:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 核心场景:多个对象相互依赖(如聊天室:用户 A→中介者→用户 B,无需用户直接通信)。
- 应用:Spring 的DispatcherServlet(作为中介者,接收请求并分发到对应的 Controller)。
4.5 状态模式(State Pattern)
- 定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
- 核心场景:对象状态多且状态改变影响行为(如订单状态:待支付→已支付→已发货→已完成)。
- 应用:Spring StateMachine(状态管理框架)、订单系统的状态流转。
第四部分:设计模式的实际应用与实践
框架中的设计模式(精选)
设计模式的选择策略
- 先原则,后模式:设计模式是原则的落地,先满足单一职责、开闭原则等,再选择模式;
- 避免过度设计:简单场景用 “if-else” 比策略模式更简洁,不要为了用模式而用模式;
- 按问题选模式:
- 对象创建复杂→工厂 / 建造者;
- 功能需动态扩展→装饰器 / 代理;
- 多算法切换→策略;
- 一对多通信→观察者;
- 流程固定 + 步骤可变→模板方法。
常见误区
- 模式滥用:用单例模式管理有状态的对象(导致线程安全问题);
- 混淆相似模式:
- 装饰器 vs 适配器:装饰器是 “增强功能”,适配器是 “转换接口”;
- 策略 vs 状态:策略是 “外部切换算法”,状态是 “内部状态驱动行为”;
- 工厂方法 vs 抽象工厂:工厂方法是 “单一产品”,抽象工厂是 “产品族”;
- 忽略原则:为了用模式而违反单一职责(如一个类既是工厂又是产品)。
总结
设计模式不是 “银弹”,它无法解决所有软件设计问题,但能为 “重复出现的问题” 提供经过验证的解决方案。学习设计模式的核心是:
- 理解设计原则(六大原则是根基);
- 掌握模式本质(每个模式解决的核心问题);
- 结合实际场景(框架源码、业务需求)灵活应用。