Java 设计模式完全指南:从入门到精通
📖 阅读说明:本文约 5 万字,预计阅读/讲解时间 20-30 分钟,适合零基础初学者。全文通过大量生活案例 + 完整代码示例,手把手带你掌握 Java 中 23 种经典设计模式。
目录
[Java 设计模式完全指南:从入门到精通](#Java 设计模式完全指南:从入门到精通)
[1. 什么是设计模式?为什么要学?](#1. 什么是设计模式?为什么要学?)
[1.1 一个有趣的比喻](#1.1 一个有趣的比喻)
[1.2 设计模式的来源](#1.2 设计模式的来源)
[1.3 为什么一定要学设计模式?](#1.3 为什么一定要学设计模式?)
[1.4 怎么学?](#1.4 怎么学?)
[2. 设计模式的六大原则](#2. 设计模式的六大原则)
[2.1 单一职责原则(SRP)](#2.1 单一职责原则(SRP))
[2.2 开放封闭原则(OCP)](#2.2 开放封闭原则(OCP))
[2.3 里氏替换原则(LSP)](#2.3 里氏替换原则(LSP))
[2.4 接口隔离原则(ISP)](#2.4 接口隔离原则(ISP))
[2.5 依赖倒置原则(DIP)](#2.5 依赖倒置原则(DIP))
[2.6 迪米特法则(LoD)](#2.6 迪米特法则(LoD))
[3. 创建型模式](#3. 创建型模式)
[3.1 单例模式(Singleton Pattern)](#3.1 单例模式(Singleton Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[🔍 实际项目中的应用](#🔍 实际项目中的应用)
[⚠️ 单例模式的注意事项](#⚠️ 单例模式的注意事项)
[3.2 工厂方法模式(Factory Method Pattern)](#3.2 工厂方法模式(Factory Method Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[🚀 简单工厂(不是GoF模式,但很常用)](#🚀 简单工厂(不是GoF模式,但很常用))
[🔍 JDK 中的工厂方法模式](#🔍 JDK 中的工厂方法模式)
[3.3 抽象工厂模式(Abstract Factory Pattern)](#3.3 抽象工厂模式(Abstract Factory Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[3.4 建造者模式(Builder Pattern)](#3.4 建造者模式(Builder Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[🔍 Lombok 的 @Builder 注解](#🔍 Lombok 的 @Builder 注解)
[🔍 JDK/框架中的 Builder 模式](#🔍 JDK/框架中的 Builder 模式)
[3.5 原型模式(Prototype Pattern)](#3.5 原型模式(Prototype Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[浅拷贝 vs 深拷贝](#浅拷贝 vs 深拷贝)
[4. 结构型模式](#4. 结构型模式)
[4.1 适配器模式(Adapter Pattern)](#4.1 适配器模式(Adapter Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[🔍 JDK 中的适配器模式](#🔍 JDK 中的适配器模式)
[4.2 桥接模式(Bridge Pattern)](#4.2 桥接模式(Bridge Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[4.3 组合模式(Composite Pattern)](#4.3 组合模式(Composite Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[4.4 装饰器模式(Decorator Pattern)](#4.4 装饰器模式(Decorator Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[🔍 Java I/O 中的装饰器模式](#🔍 Java I/O 中的装饰器模式)
[4.5 外观模式(Facade Pattern)](#4.5 外观模式(Facade Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[4.6 享元模式(Flyweight Pattern)](#4.6 享元模式(Flyweight Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[🔍 JDK 中的享元模式](#🔍 JDK 中的享元模式)
[4.7 代理模式(Proxy Pattern)](#4.7 代理模式(Proxy Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 静态代理](#💻 静态代理)
[💻 动态代理(JDK)](#💻 动态代理(JDK))
[💻 CGLIB 代理(Spring 用于没有接口的类)](#💻 CGLIB 代理(Spring 用于没有接口的类))
[JDK代理 vs CGLIB代理](#JDK代理 vs CGLIB代理)
[5. 行为型模式](#5. 行为型模式)
[5.1 责任链模式(Chain of Responsibility Pattern)](#5.1 责任链模式(Chain of Responsibility Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[5.2 命令模式(Command Pattern)](#5.2 命令模式(Command Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[5.3 迭代器模式(Iterator Pattern)](#5.3 迭代器模式(Iterator Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[5.4 中介者模式(Mediator Pattern)](#5.4 中介者模式(Mediator Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[5.5 备忘录模式(Memento Pattern)](#5.5 备忘录模式(Memento Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[5.6 观察者模式(Observer Pattern)](#5.6 观察者模式(Observer Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[🔍 Spring 中的观察者模式](#🔍 Spring 中的观察者模式)
[5.7 状态模式(State Pattern)](#5.7 状态模式(State Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[5.8 策略模式(Strategy Pattern)](#5.8 策略模式(Strategy Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[5.9 模板方法模式(Template Method Pattern)](#5.9 模板方法模式(Template Method Pattern))
[🍕 生活例子](#🍕 生活例子)
[📋 使用场景](#📋 使用场景)
[💻 代码实现](#💻 代码实现)
[5.10 访问者模式(Visitor Pattern)](#5.10 访问者模式(Visitor Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[5.11 解释器模式(Interpreter Pattern)](#5.11 解释器模式(Interpreter Pattern))
[🍕 生活例子](#🍕 生活例子)
[💻 代码实现](#💻 代码实现)
[6. 设计模式在主流框架中的应用](#6. 设计模式在主流框架中的应用)
[6.1 Spring 框架中的设计模式](#6.1 Spring 框架中的设计模式)
[工厂模式:BeanFactory / ApplicationContext](#工厂模式:BeanFactory / ApplicationContext)
[单例模式:Spring Bean 默认作用域](#单例模式:Spring Bean 默认作用域)
[代理模式:Spring AOP](#代理模式:Spring AOP)
[观察者模式:Spring 事件机制](#观察者模式:Spring 事件机制)
装饰器模式:HttpServletRequestWrapper
[6.2 MyBatis 中的设计模式](#6.2 MyBatis 中的设计模式)
建造者模式:SqlSessionFactoryBuilder
[代理模式:Mapper 接口](#代理模式:Mapper 接口)
[6.3 JDK 中的设计模式总结](#6.3 JDK 中的设计模式总结)
[7. 总结与学习建议](#7. 总结与学习建议)
[7.1 23种设计模式速查表](#7.1 23种设计模式速查表)
[7.2 哪些模式最常用?](#7.2 哪些模式最常用?)
[7.3 如何避免过度设计?](#7.3 如何避免过度设计?)
[7.4 学习路线建议](#7.4 学习路线建议)
[7.5 推荐资源](#7.5 推荐资源)
[7.6 一段代码结束全文](#7.6 一段代码结束全文)
[A.1 综合案例:设计一个简易 RPC 框架](#A.1 综合案例:设计一个简易 RPC 框架)
[A.2 综合案例:设计一个插件化系统](#A.2 综合案例:设计一个插件化系统)
[B.1 关于单例模式](#B.1 关于单例模式)
[B.2 关于工厂模式](#B.2 关于工厂模式)
[B.3 关于代理模式](#B.3 关于代理模式)
[B.4 关于观察者模式](#B.4 关于观察者模式)
[B.5 关于策略模式](#B.5 关于策略模式)
[附录D:用 Java 8+ 特性简化设计模式](#附录D:用 Java 8+ 特性简化设计模式)
[E.1 贫血模型(Anti-Pattern)](#E.1 贫血模型(Anti-Pattern))
[E.2 万能类(God Object Anti-Pattern)](#E.2 万能类(God Object Anti-Pattern))
[E.3 过度设计(Over-Engineering Anti-Pattern)](#E.3 过度设计(Over-Engineering Anti-Pattern))
1. 什么是设计模式?为什么要学?
1.1 一个有趣的比喻
想象一下,你是一位厨师。每次做番茄炒蛋,你都从零开始思考:"先放油还是先打蛋?要不要先把番茄切好?"这样每次都要重新想,非常低效。
但如果有一本菜谱,上面写清楚了步骤:先热锅倒油 → 炒鸡蛋盛出 → 炒番茄 → 鸡蛋回锅 → 调味出锅,那每次照着做就行了,又快又好。
设计模式就是软件开发中的"菜谱"。
它是前人(软件大师们)在长期开发实践中总结出来的、针对特定场景 下的最佳解决方案。
1.2 设计模式的来源
1994年,四位软件大师(Gang of Four,简称 GoF ,四人帮)出版了一本划时代的书:《设计模式:可复用面向对象软件的基础》。
书中总结了面向对象设计中最常见的 23 种设计模式,按用途分为三大类:
| 类别 | 数量 | 作用 |
|---|---|---|
| 创建型模式 | 5种 | 解决对象的创建问题 |
| 结构型模式 | 7种 | 解决对象的组合问题 |
| 行为型模式 | 11种 | 解决对象的通信问题 |
1.3 为什么一定要学设计模式?
原因一:代码质量飞跃
没学设计模式之前,你的代码可能是这样的:
// ❌ 糟糕的代码:一个方法做了所有的事情
public void processOrder(Order order) {
// 验证订单
if (order == null) throw new RuntimeException("订单不能为空");
if (order.getItems().isEmpty()) throw new RuntimeException("订单不能为空");
// 计算价格
double total = 0;
for (Item item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// 支付
if (order.getPayType().equals("alipay")) {
// 支付宝支付逻辑...
} else if (order.getPayType().equals("wechat")) {
// 微信支付逻辑...
}
// 发送通知
// 发短信...
// 发邮件...
// 发APP推送...
}
学了设计模式之后,你会把它拆分成优雅的结构,每个部分各司其职。
原因二:和同事沟通更顺畅
当你说"这里用策略模式",懂设计模式的同事立刻就明白了你的意图,不需要长篇大论解释。设计模式是程序员之间的共同语言。
原因三:看懂主流框架
Spring、MyBatis、JDK 源码中大量使用设计模式。学了才能看懂框架的设计思路:
- Spring IoC → 工厂模式
- Spring AOP → 代理模式
- Spring 事件 → 观察者模式
- MyBatis SqlSession → 模板方法模式
- JDK Collections → 迭代器模式
原因四:面试必考
设计模式是中高级 Java 工程师面试的必考内容。
1.4 怎么学?
本文的学习路径:概念 → 生活例子 → 代码例子 → 实际应用
每个模式我们都会用最通俗的语言解释,配合生活中的类比,让你一看就懂。
2. 设计模式的六大原则
学设计模式之前,必须先了解面向对象设计的六大原则,因为所有设计模式都是基于这些原则来的。
2.1 单一职责原则(SRP)
一个类只负责一件事。
就像公司里,财务只管财务,人事只管人事,不能一个人包揽所有。
// ❌ 违反单一职责:一个类既管用户数据,又管邮件发送
public class UserService {
public void saveUser(User user) {
// 保存用户到数据库
}
public void sendWelcomeEmail(User user) {
// 发送欢迎邮件
}
}
// ✅ 遵循单一职责:分开成两个类
public class UserService {
public void saveUser(User user) {
// 只负责保存用户
}
}
public class EmailService {
public void sendWelcomeEmail(User user) {
// 只负责发邮件
}
}
2.2 开放封闭原则(OCP)
对扩展开放,对修改封闭。
意思是:当需要添加新功能时,应该通过新增代码来实现,而不是修改已有代码。
就像手机充电接口,以前用 USB-A,现在用 Type-C,你不需要拆开手机改电路,只需要换个数据线(扩展)就行。
// ❌ 违反开放封闭:每次新增支付方式都要修改这个方法
public class PaymentService {
public void pay(String type, double amount) {
if (type.equals("alipay")) {
// 支付宝支付
} else if (type.equals("wechat")) {
// 微信支付
}
// 新增银联支付?只能修改这里,风险很大!
}
}
// ✅ 遵循开放封闭:定义接口,新增支付方式只需新增类
public interface Payment {
void pay(double amount);
}
public class AlipayPayment implements Payment {
public void pay(double amount) { /* 支付宝逻辑 */ }
}
public class WechatPayment implements Payment {
public void pay(double amount) { /* 微信支付逻辑 */ }
}
// 新增银联支付,完全不用修改已有代码!
public class UnionpayPayment implements Payment {
public void pay(double amount) { /* 银联逻辑 */ }
}
2.3 里氏替换原则(LSP)
子类必须能替换父类。
简单说:父类能做的事,子类也必须能做,而且不能破坏父类的行为。
// ❌ 违反里氏替换
public class Rectangle {
protected int width, height;
public void setWidth(int w) { this.width = w; }
public void setHeight(int h) { this.height = h; }
public int getArea() { return width * height; }
}
// 正方形继承矩形,但破坏了矩形的行为!
public class Square extends Rectangle {
@Override
public void setWidth(int w) {
this.width = w;
this.height = w; // 正方形宽高必须相等,但这破坏了父类的行为
}
}
// 用矩形的地方替换成正方形,行为不一样了!
Rectangle r = new Square();
r.setWidth(5);
r.setHeight(10);
System.out.println(r.getArea()); // 期望 50,实际输出 100!
2.4 接口隔离原则(ISP)
接口不应该太胖,客户端不应该依赖它不需要的方法。
就像餐厅菜单,不应该把所有菜(早餐、午餐、晚餐、饮料)塞进一张菜单,应该分开。
// ❌ 接口太胖
public interface Worker {
void work();
void eat();
void sleep();
}
// 机器人不需要吃饭和睡觉,但被迫实现这两个方法
public class Robot implements Worker {
public void work() { /* 工作 */ }
public void eat() { /* 机器人不吃饭,空实现 */ }
public void sleep() { /* 机器人不睡觉,空实现 */ }
}
// ✅ 接口分离
public interface Workable { void work(); }
public interface Eatable { void eat(); }
public interface Sleepable { void sleep(); }
public class Human implements Workable, Eatable, Sleepable {
public void work() { }
public void eat() { }
public void sleep() { }
}
public class Robot implements Workable {
public void work() { } // 只实现需要的接口
}
2.5 依赖倒置原则(DIP)
高层模块不应该依赖低层模块,两者都应该依赖抽象。
就像电源插座,你的电器不应该直接焊死在墙上,而是通过标准插头(抽象接口)来连接。
// ❌ 高层依赖低层(具体实现)
public class OrderService {
private MySQLOrderDao dao = new MySQLOrderDao(); // 直接依赖MySQL实现
public void saveOrder(Order order) {
dao.save(order);
}
}
// ✅ 依赖抽象接口
public interface OrderDao {
void save(Order order);
}
public class MySQLOrderDao implements OrderDao {
public void save(Order order) { /* MySQL实现 */ }
}
public class MongoOrderDao implements OrderDao {
public void save(Order order) { /* MongoDB实现 */ }
}
public class OrderService {
private OrderDao dao; // 依赖接口,不依赖具体实现
// 通过构造器注入(Spring会帮你做这件事)
public OrderService(OrderDao dao) {
this.dao = dao;
}
public void saveOrder(Order order) {
dao.save(order); // 切换数据库,这里的代码完全不用改
}
}
2.6 迪米特法则(LoD)
一个对象应该对其他对象了解最少。
又叫"最少知识原则"。就像你去书店买书,你只需要告诉收银员"我要这本书",你不需要知道书是怎么进货的、仓库在哪里。
// ❌ 违反迪米特:A 直接操作 C 的内部
public class A {
public void doSomething(B b) {
C c = b.getC();
c.doSomethingElse(); // A 不应该直接调用 C 的方法
}
}
// ✅ 遵循迪米特:A 只和直接朋友 B 交互
public class B {
private C c = new C();
public void doSomethingWithC() {
c.doSomethingElse(); // B 帮 A 去调用 C
}
}
public class A {
public void doSomething(B b) {
b.doSomethingWithC(); // A 只和 B 交互
}
}
3. 创建型模式
创建型模式关注对象的创建过程,把对象的创建和使用分离,让系统更灵活。
3.1 单例模式(Singleton Pattern)
🍕 生活例子
全国只有一个国务院,不可能有两个。程序中有些对象也只需要一个,比如配置管理器、数据库连接池、日志管理器等。单例模式确保一个类只有一个实例,并提供全局访问点。
📋 使用场景
- 线程池、数据库连接池
- 配置文件读取器
- 日志记录器
- Spring 中的 Bean(默认单例)
💻 代码实现
方式1:饿汉式(类加载时就创建,线程安全)
public class Singleton {
// 类加载时就创建实例,"饿汉"迫不及待,一上来就准备好
private static final Singleton INSTANCE = new Singleton();
// 私有构造器,外部无法 new 对象
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
return INSTANCE;
}
public void doSomething() {
System.out.println("单例对象做了些事情");
}
}
// 使用
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true,是同一个对象
优点 :实现简单,线程安全 缺点:无论用不用都会创建实例,浪费内存(如果对象很重)
方式2:懒汉式(用到时才创建,需要考虑线程安全)
// ❌ 线程不安全的懒汉式
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
// 多线程下可能有多个线程同时进入这里,创建多个实例!
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
方式3:双重检查锁(DCL,推荐)
public class Singleton {
// volatile 防止指令重排序
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(不加锁,提高性能)
synchronized (Singleton.class) { // 加锁
if (instance == null) { // 第二次检查(防止重复创建)
instance = new Singleton();
}
}
}
return instance;
}
}
为什么要两次 if 判断?
想象两个线程 A 和 B 同时进入了第一个 if,A 先拿到锁创建了实例,然后释放锁。如果没有第二个 if,B 拿到锁后会再创建一个实例!第二个 if 就是为了防止这种情况。
方式4:静态内部类(最优雅,推荐)
public class Singleton {
private Singleton() {}
// 静态内部类:只有在第一次被使用时,JVM 才会加载它
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
为什么这个好?
- 懒加载 :内部类只在第一次调用
getInstance()时才加载 - 线程安全:由 JVM 的类加载机制保证,天然线程安全
- 代码简洁:不需要 synchronized,性能好
方式5:枚举单例(防反射攻击,最安全)
public enum Singleton {
INSTANCE; // 就这一行!
public void doSomething() {
System.out.println("枚举单例做了些事情");
}
}
// 使用
Singleton.INSTANCE.doSomething();
这是《Effective Java》作者 Joshua Bloch 推荐的方式,能防止反射和序列化攻击破坏单例。
🔍 实际项目中的应用
// 实际项目中的配置管理器单例
public class ConfigManager {
private static volatile ConfigManager instance;
private Properties props;
private ConfigManager() {
props = new Properties();
try {
// 加载配置文件
InputStream is = getClass().getResourceAsStream("/application.properties");
props.load(is);
} catch (IOException e) {
throw new RuntimeException("加载配置文件失败", e);
}
}
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}
public String get(String key) {
return props.getProperty(key);
}
public String get(String key, String defaultValue) {
return props.getProperty(key, defaultValue);
}
}
// 在任意地方使用
String dbUrl = ConfigManager.getInstance().get("db.url");
String port = ConfigManager.getInstance().get("server.port", "8080");
⚠️ 单例模式的注意事项
- 多线程环境:必须保证线程安全
- 反射攻击:可以通过反射调用私有构造器破坏单例,枚举方式能防止
- 序列化 :反序列化会创建新对象,需要实现
readResolve()方法 - 单元测试:单例难以 Mock,影响测试,要谨慎使用
3.2 工厂方法模式(Factory Method Pattern)
🍕 生活例子
你去麦当劳,不管点什么汉堡,你都说"给我来一个汉堡",麦当劳后厨(工厂)负责制作。你不需要知道汉堡是怎么做的,直接吃就行。
工厂方法模式:定义一个创建对象的接口,让子类决定实例化哪个类。
📋 使用场景
- 当你不知道该创建哪个具体类时
- 当你想让子类来决定创建什么对象时
- 日志框架(Log4j、SLF4J)的 Logger 创建
💻 代码实现
场景:开发一个支持多种数据库的 DAO 层
// 第一步:定义产品接口
public interface UserDao {
void save(User user);
User findById(int id);
void delete(int id);
}
// 第二步:实现具体产品
public class MySQLUserDao implements UserDao {
@Override
public void save(User user) {
System.out.println("MySQL: 保存用户 " + user.getName());
// 实际的MySQL操作...
}
@Override
public User findById(int id) {
System.out.println("MySQL: 查询用户 id=" + id);
return new User(id, "张三"); // 模拟从MySQL查询
}
@Override
public void delete(int id) {
System.out.println("MySQL: 删除用户 id=" + id);
}
}
public class OracleUserDao implements UserDao {
@Override
public void save(User user) {
System.out.println("Oracle: 保存用户 " + user.getName());
}
@Override
public User findById(int id) {
System.out.println("Oracle: 查询用户 id=" + id);
return new User(id, "李四");
}
@Override
public void delete(int id) {
System.out.println("Oracle: 删除用户 id=" + id);
}
}
// 第三步:定义工厂接口
public interface DaoFactory {
UserDao createUserDao();
}
// 第四步:实现具体工厂
public class MySQLDaoFactory implements DaoFactory {
@Override
public UserDao createUserDao() {
// 可以在这里初始化MySQL连接、配置等
return new MySQLUserDao();
}
}
public class OracleDaoFactory implements DaoFactory {
@Override
public UserDao createUserDao() {
return new OracleUserDao();
}
}
// 使用
public class Main {
public static void main(String[] args) {
// 只需要换一行,切换整个数据库!
DaoFactory factory = new MySQLDaoFactory();
// DaoFactory factory = new OracleDaoFactory(); // 切换到Oracle
UserDao userDao = factory.createUserDao();
userDao.save(new User(1, "张三"));
User user = userDao.findById(1);
System.out.println("找到用户:" + user.getName());
}
}
🚀 简单工厂(不是GoF模式,但很常用)
// 简单工厂:用一个工厂类,根据参数创建不同对象
public class LoggerFactory {
public static Logger getLogger(String type) {
switch (type) {
case "file": return new FileLogger();
case "db": return new DatabaseLogger();
case "console": return new ConsoleLogger();
default: throw new IllegalArgumentException("未知的日志类型:" + type);
}
}
}
// 使用
Logger logger = LoggerFactory.getLogger("file");
logger.log("应用启动");
简单工厂 vs 工厂方法的区别:
| 简单工厂 | 工厂方法 | |
|---|---|---|
| 违反OCP吗? | 是(新增类型要改switch) | 否(新增类型只需新增类) |
| 代码复杂度 | 低 | 稍高 |
| 使用场景 | 产品类型固定、不常变化 | 产品类型会扩展 |
🔍 JDK 中的工厂方法模式
// Calendar.getInstance() 就是工厂方法
Calendar calendar = Calendar.getInstance();
// NumberFormat.getInstance()
NumberFormat format = NumberFormat.getInstance();
// JDBC 中的工厂方法
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement(); // Connection 是工厂,Statement 是产品
3.3 抽象工厂模式(Abstract Factory Pattern)
🍕 生活例子
你要装修房间,可以选择"中式风格"或"欧式风格"。
选了中式风格,家具(沙发、床、桌子)都是中式的;选了欧式风格,所有家具都是欧式的。你不会中式沙发配欧式床------这就是产品族的概念。
抽象工厂模式:创建一系列相关或相互依赖的对象(产品族),而不需要指定具体类。
📋 使用场景
- 系统要独立于它的产品的创建、组合和表示
- 系统要由多个产品系列中的一个来配置
- 跨数据库开发(需要一套配套的DAO)
💻 代码实现
场景:开发一个UI组件库,支持Windows和Mac两种风格
// 产品接口
public interface Button {
void click();
void render();
}
public interface TextBox {
void input(String text);
String getValue();
}
public interface Dialog {
void show(String message);
}
// Windows 风格的具体产品
public class WindowsButton implements Button {
@Override
public void click() { System.out.println("Windows按钮被点击,带有Windows点击特效"); }
@Override
public void render() { System.out.println("渲染Windows风格按钮(矩形、灰色边框)"); }
}
public class WindowsTextBox implements TextBox {
private String value = "";
@Override
public void input(String text) {
this.value = text;
System.out.println("Windows文本框输入: " + text);
}
@Override
public String getValue() { return value; }
}
public class WindowsDialog implements Dialog {
@Override
public void show(String message) {
System.out.println("Windows弹窗: [" + message + "] [确定] [取消]");
}
}
// Mac 风格的具体产品
public class MacButton implements Button {
@Override
public void click() { System.out.println("Mac按钮被点击,带有Mac点击特效和声音"); }
@Override
public void render() { System.out.println("渲染Mac风格按钮(圆角、阴影效果)"); }
}
public class MacTextBox implements TextBox {
private String value = "";
@Override
public void input(String text) {
this.value = text;
System.out.println("Mac文本框输入: " + text);
}
@Override
public String getValue() { return value; }
}
public class MacDialog implements Dialog {
@Override
public void show(String message) {
System.out.println("Mac弹窗: ○ " + message + " [好]");
}
}
// 抽象工厂接口(创建一整套配套组件)
public interface UIFactory {
Button createButton();
TextBox createTextBox();
Dialog createDialog();
}
// Windows 工厂(创建一套Windows风格组件)
public class WindowsUIFactory implements UIFactory {
@Override
public Button createButton() { return new WindowsButton(); }
@Override
public TextBox createTextBox() { return new WindowsTextBox(); }
@Override
public Dialog createDialog() { return new WindowsDialog(); }
}
// Mac 工厂(创建一套Mac风格组件)
public class MacUIFactory implements UIFactory {
@Override
public Button createButton() { return new MacButton(); }
@Override
public TextBox createTextBox() { return new MacTextBox(); }
@Override
public Dialog createDialog() { return new MacDialog(); }
}
// 应用程序使用抽象工厂,完全不知道具体是哪个平台
public class Application {
private UIFactory factory;
private Button button;
private TextBox textBox;
private Dialog dialog;
public Application(UIFactory factory) {
this.factory = factory;
}
public void createUI() {
button = factory.createButton();
textBox = factory.createTextBox();
dialog = factory.createDialog();
}
public void run() {
button.render();
textBox.input("Hello World");
button.click();
dialog.show("操作成功!");
}
}
// 主程序:根据操作系统选择工厂
public class Main {
public static void main(String[] args) {
UIFactory factory;
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("mac")) {
factory = new MacUIFactory();
} else {
factory = new WindowsUIFactory();
}
Application app = new Application(factory);
app.createUI();
app.run();
}
}
运行结果(Windows系统):
渲染Windows风格按钮(矩形、灰色边框)
Windows文本框输入: Hello World
Windows按钮被点击,带有Windows点击特效
Windows弹窗: [操作成功!] [确定] [取消]
工厂模式三兄弟对比
| 模式 | 核心思想 | 适用场景 |
|---|---|---|
| 简单工厂 | 一个工厂类,if-else 决定创建哪个 | 产品较少,不常变化 |
| 工厂方法 | 每个产品对应一个工厂 | 产品会扩展 |
| 抽象工厂 | 工厂能创建一系列相关产品 | 有多个产品族,需要保证产品一致性 |
3.4 建造者模式(Builder Pattern)
🍕 生活例子
你去汉堡店点餐,服务员问你:要什么面包?要什么肉?要什么酱料?要什么蔬菜?
同样是汉堡,可以有无数种组合。建造者(服务员)按你的要求一步步组装。
建造者模式:将一个复杂对象的构建和它的表示分离,同样的构建过程可以创建不同的表示。
📋 使用场景
- 创建的对象有很多参数(超过4个以上的参数,建议用 Builder)
- 对象的创建步骤很多,且步骤顺序可能不同
- Lombok 的
@Builder注解底层就是这个模式 - StringBuilder、HttpClient、AlertDialog
💻 代码实现
场景:创建一个复杂的 HTTP 请求对象
// ❌ 不用Builder模式的痛苦(构造器参数太多,根本记不住顺序)
HttpRequest request = new HttpRequest(
"https://api.example.com", // url
"POST", // method
"application/json", // contentType
30000, // timeout
true, // followRedirects
Map.of("Authorization", "Bearer xxx"), // headers
"{\"name\": \"张三\"}", // body
"UTF-8", // charset
true, // compress
3 // retryCount
);
// 谁能记住这10个参数的顺序?!
// ✅ 用Builder模式,清晰优雅
public class HttpRequest {
// 所有字段
private final String url;
private final String method;
private final String contentType;
private final int timeout;
private final boolean followRedirects;
private final Map<String, String> headers;
private final String body;
private final String charset;
private final boolean compress;
private final int retryCount;
// 私有构造器,只能通过Builder创建
private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.contentType = builder.contentType;
this.timeout = builder.timeout;
this.followRedirects = builder.followRedirects;
this.headers = builder.headers;
this.body = builder.body;
this.charset = builder.charset;
this.compress = builder.compress;
this.retryCount = builder.retryCount;
}
// getters...
public String getUrl() { return url; }
public String getMethod() { return method; }
// ... 其他 getter
@Override
public String toString() {
return String.format("HttpRequest{url='%s', method='%s', timeout=%d, charset='%s'}",
url, method, timeout, charset);
}
// 静态内部 Builder 类
public static class Builder {
// 必填参数
private final String url;
private String method = "GET"; // 可选参数,有默认值
// 可选参数(带默认值)
private String contentType = "application/json";
private int timeout = 5000;
private boolean followRedirects = true;
private Map<String, String> headers = new HashMap<>();
private String body;
private String charset = "UTF-8";
private boolean compress = false;
private int retryCount = 0;
// 必填参数通过构造器传入
public Builder(String url) {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("URL不能为空");
}
this.url = url;
}
// 每个 setter 方法返回 Builder 自身,实现链式调用
public Builder method(String method) {
this.method = method;
return this;
}
public Builder contentType(String contentType) {
this.contentType = contentType;
return this;
}
public Builder timeout(int timeout) {
if (timeout <= 0) throw new IllegalArgumentException("超时时间必须大于0");
this.timeout = timeout;
return this;
}
public Builder followRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}
public Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder charset(String charset) {
this.charset = charset;
return this;
}
public Builder compress(boolean compress) {
this.compress = compress;
return this;
}
public Builder retryCount(int retryCount) {
this.retryCount = retryCount;
return this;
}
// build() 方法创建最终对象
public HttpRequest build() {
// 可以在这里做最终校验
if ("POST".equals(method) || "PUT".equals(method)) {
if (body == null || body.isEmpty()) {
System.out.println("警告:POST/PUT请求没有请求体");
}
}
return new HttpRequest(this);
}
}
}
// 优雅的链式调用!
HttpRequest request = new HttpRequest.Builder("https://api.example.com/users")
.method("POST")
.contentType("application/json")
.timeout(30000)
.header("Authorization", "Bearer eyJhbGci...")
.header("X-Request-ID", "uuid-123")
.body("{\"name\": \"张三\", \"age\": 25}")
.retryCount(3)
.build();
System.out.println(request);
// 输出: HttpRequest{url='https://api.example.com/users', method='POST', timeout=30000, charset='UTF-8'}
🔍 Lombok 的 @Builder 注解
在实际项目中,我们通常用 Lombok 来自动生成 Builder:
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private Long id;
private String name;
private String email;
private Integer age;
private String phone;
}
// Lombok 会自动生成 Builder 代码
User user = User.builder()
.id(1L)
.name("张三")
.email("zhangsan@example.com")
.age(25)
.phone("138xxxxxxxx")
.build();
System.out.println(user); // User(id=1, name=张三, email=zhangsan@example.com, age=25, phone=138xxxxxxxx)
🔍 JDK/框架中的 Builder 模式
// StringBuilder
String result = new StringBuilder()
.append("Hello")
.append(", ")
.append("World")
.append("!")
.toString();
// Java 8 Stream
List<String> result = list.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// HttpClient(Java 11+)
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(10))
.build();
3.5 原型模式(Prototype Pattern)
🍕 生活例子
你画了一幅画,朋友看到后想要一幅一模一样的。你不会重新画一幅,而是复印(克隆)一份给他。
原型模式:通过复制(克隆)现有对象来创建新对象,而不是通过 new 关键字。
📋 使用场景
- 对象创建成本很高(数据库查询、网络请求、复杂计算)
- 需要创建大量相似对象
- 游戏中复制怪物、子弹等大量相似对象
💻 代码实现
// Java 中使用 Cloneable 接口实现原型模式
public class GameCharacter implements Cloneable {
private String name;
private int health;
private int attack;
private int defense;
private List<String> skills; // 注意:这是引用类型,需要深拷贝
public GameCharacter(String name, int health, int attack, int defense) {
this.name = name;
this.health = health;
this.attack = attack;
this.defense = defense;
this.skills = new ArrayList<>();
}
public void addSkill(String skill) {
skills.add(skill);
}
// 浅拷贝(Shallow Copy)
@Override
protected GameCharacter clone() throws CloneNotSupportedException {
return (GameCharacter) super.clone();
// 注意:浅拷贝中,skills 列表仍然是共享的!
}
// 深拷贝(Deep Copy):完全独立的新对象
public GameCharacter deepClone() {
GameCharacter clone = new GameCharacter(
this.name, this.health, this.attack, this.defense
);
// 手动复制引用类型的字段
clone.skills = new ArrayList<>(this.skills);
return clone;
}
@Override
public String toString() {
return String.format("GameCharacter{name='%s', HP=%d, ATK=%d, DEF=%d, skills=%s}",
name, health, attack, defense, skills);
}
// getters and setters
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public int getHealth() { return health; }
}
// 使用原型模式快速创建大量相似对象
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建一个"精英怪"原型(假设创建过程很耗时)
GameCharacter eliteMonster = new GameCharacter("精英哥布林", 1000, 150, 80);
eliteMonster.addSkill("毒刺攻击");
eliteMonster.addSkill("召唤小怪");
System.out.println("原型: " + eliteMonster);
// 浅拷贝演示
GameCharacter monster1 = eliteMonster.clone();
monster1.setName("精英哥布林-1号");
// 深拷贝:技能列表是独立的
GameCharacter monster2 = eliteMonster.deepClone();
monster2.setName("精英哥布林-2号");
monster2.addSkill("烈焰冲撞"); // 只给2号加新技能
System.out.println("1号: " + monster1);
System.out.println("2号: " + monster2);
System.out.println("原型: " + eliteMonster); // 原型的技能不受影响
}
}
输出:
原型: GameCharacter{name='精英哥布林', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪]}
1号: GameCharacter{name='精英哥布林-1号', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪]}
2号: GameCharacter{name='精英哥布林-2号', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪, 烈焰冲撞]}
原型: GameCharacter{name='精英哥布林', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪]}
浅拷贝 vs 深拷贝
原型对象: [name="精英哥布林"] [health=1000] [skills → List["毒刺","召唤"]]
↑
浅拷贝: [name="1号怪"] [health=1000] [skills → ](共享同一个List!)
↑
深拷贝: [name="2号怪"] [health=1000] [skills → List["毒刺","召唤"]](独立的新List!)
4. 结构型模式
结构型模式关注如何将类或对象组合成更大的结构,就像搭积木一样,把小零件拼成大玩意儿。
4.1 适配器模式(Adapter Pattern)
🍕 生活例子
你买了一个美国电器,插头是两脚扁头的,而中国插座是三脚圆头的。你需要一个电源适配器才能用。
适配器模式:将一个类的接口转换成客户期望的另一个接口,让不兼容的接口可以一起工作。
📋 使用场景
- 整合旧系统(Legacy System)
- 使用第三方库,但接口不匹配
- JDK 中:
Arrays.asList()、Collections.enumeration()、InputStreamReader
💻 代码实现
场景:你的系统用 MediaPlayer 接口,但想使用只支持 AdvancedPlayer 接口的第三方库
// 你的系统定义的接口(目标接口)
public interface MediaPlayer {
void play(String fileName);
}
// 第三方库的接口(被适配者)
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
// 第三方库的实现(你无法修改)
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("用VLC播放器播放: " + fileName);
}
@Override
public void playMp4(String fileName) {
// VLC播放器不支持mp4
}
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// Mp4播放器不支持vlc
}
@Override
public void playMp4(String fileName) {
System.out.println("用Mp4播放器播放: " + fileName);
}
}
// 适配器:让 AdvancedMediaPlayer 适配 MediaPlayer 接口
public class MediaPlayerAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedPlayer;
public MediaPlayerAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedPlayer = new Mp4Player();
}
}
@Override
public void play(String fileName) {
// 将 MediaPlayer.play() 转换成 AdvancedMediaPlayer 的方法
if (advancedPlayer instanceof VlcPlayer) {
advancedPlayer.playVlc(fileName);
} else if (advancedPlayer instanceof Mp4Player) {
advancedPlayer.playMp4(fileName);
}
}
}
// 你的播放器(使用你定义的接口)
public class AudioPlayer implements MediaPlayer {
private MediaPlayerAdapter adapter;
@Override
public void play(String fileName) {
// 获取文件扩展名
String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
if (extension.equalsIgnoreCase("mp3")) {
System.out.println("直接播放mp3: " + fileName);
} else if (extension.equalsIgnoreCase("vlc") || extension.equalsIgnoreCase("mp4")) {
// 通过适配器使用第三方播放器
adapter = new MediaPlayerAdapter(extension);
adapter.play(fileName);
} else {
System.out.println("不支持的格式:" + extension);
}
}
}
// 测试
public class Main {
public static void main(String[] args) {
AudioPlayer player = new AudioPlayer();
player.play("my_song.mp3"); // 直接播放mp3
player.play("movie.mp4"); // 通过适配器用Mp4Player播放
player.play("documentary.vlc"); // 通过适配器用VlcPlayer播放
player.play("audio.avi"); // 不支持
}
}
输出:
直接播放mp3: my_song.mp3
用Mp4播放器播放: movie.mp4
用VLC播放器播放: documentary.vlc
不支持的格式:avi
🔍 JDK 中的适配器模式
// 1. Arrays.asList() --- 将数组适配成 List
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array); // 数组 → List 的适配
// 2. InputStreamReader --- 将字节流适配成字符流
InputStream inputStream = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(inputStream, "UTF-8"); // 字节流 → 字符流
// 3. Collections.enumeration() --- 将 Collection 适配成 Enumeration
List<String> list2 = new ArrayList<>(Arrays.asList("a", "b", "c"));
Enumeration<String> enumeration = Collections.enumeration(list2);
4.2 桥接模式(Bridge Pattern)
🍕 生活例子
画图时有两个维度:形状 (圆形、方形)和颜色(红色、蓝色)。
如果用继承来实现,你需要:红圆、蓝圆、红方、蓝方------4个类。 如果再加一个颜色或形状,类的数量会爆炸!
桥接模式用组合代替继承,让两个维度独立变化。
桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
💻 代码实现
// 实现层接口(颜色)
public interface Color {
String getColor();
void fill();
}
// 具体颜色实现
public class Red implements Color {
@Override
public String getColor() { return "红色"; }
@Override
public void fill() { System.out.println("填充红色"); }
}
public class Blue implements Color {
@Override
public String getColor() { return "蓝色"; }
@Override
public void fill() { System.out.println("填充蓝色"); }
}
public class Green implements Color {
@Override
public String getColor() { return "绿色"; }
@Override
public void fill() { System.out.println("填充绿色"); }
}
// 抽象层(形状),持有对颜色的引用(这就是"桥")
public abstract class Shape {
// 持有颜色接口的引用,就是"桥"
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
// 具体形状实现
public class Circle extends Shape {
private int radius;
public Circle(Color color, int radius) {
super(color);
this.radius = radius;
}
@Override
public void draw() {
System.out.println("画一个" + color.getColor() + "圆形,半径=" + radius);
color.fill();
}
}
public class Rectangle extends Shape {
private int width, height;
public Rectangle(Color color, int width, int height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("画一个" + color.getColor() + "矩形," + width + "×" + height);
color.fill();
}
}
// 测试:形状和颜色可以自由组合!
public class Main {
public static void main(String[] args) {
// 红色圆形
Shape redCircle = new Circle(new Red(), 10);
redCircle.draw();
// 蓝色矩形
Shape blueRect = new Rectangle(new Blue(), 20, 30);
blueRect.draw();
// 绿色圆形
Shape greenCircle = new Circle(new Green(), 5);
greenCircle.draw();
// 新增一种颜色(黄色)只需新增 Yellow 类,不用改任何形状类!
// 新增一种形状(三角形)只需新增 Triangle 类,不用改任何颜色类!
}
}
输出:
画一个红色圆形,半径=10
填充红色
画一个蓝色矩形,20×30
填充蓝色
画一个绿色圆形,半径=5
填充绿色
桥接模式的核心思想:
没有桥接(继承地狱):
Shape
├── RedCircle
├── BlueCircle
├── GreenCircle
├── RedRectangle
├── BlueRectangle
└── GreenRectangle (N种形状 × M种颜色 = N×M个类)
使用桥接(组合):
Shape ─────桥────→ Color
├── Circle ├── Red
└── Rectangle ├── Blue
└── Green
(N种形状 + M种颜色 = N+M个类,自由组合!)
4.3 组合模式(Composite Pattern)
🍕 生活例子
你的文件系统:有文件夹,文件夹里有文件,文件夹里还可以有子文件夹,子文件夹里还有文件...
无论是文件还是文件夹,你都可以对它执行"计算大小"的操作。这就是组合模式。
组合模式:将对象组合成树形结构来表示"部分-整体"的层次结构,让客户端以一致的方式对待单个对象和组合对象。
💻 代码实现
// 组件接口(文件和文件夹的共同抽象)
public abstract class FileSystemItem {
protected String name;
public FileSystemItem(String name) {
this.name = name;
}
public String getName() { return name; }
// 抽象方法,文件和文件夹都要实现
public abstract long getSize();
public abstract void display(int indent);
// 文件夹特有操作(叶子节点默认不支持,可以抛异常或空实现)
public void add(FileSystemItem item) {
throw new UnsupportedOperationException("文件不支持添加子项");
}
public void remove(FileSystemItem item) {
throw new UnsupportedOperationException("文件不支持删除子项");
}
}
// 叶子节点:文件
public class File extends FileSystemItem {
private long size;
public File(String name, long size) {
super(name);
this.size = size;
}
@Override
public long getSize() { return size; }
@Override
public void display(int indent) {
System.out.println(" ".repeat(indent) + "📄 " + name + " (" + size + " KB)");
}
}
// 树枝节点:文件夹
public class Folder extends FileSystemItem {
private List<FileSystemItem> children = new ArrayList<>();
public Folder(String name) {
super(name);
}
@Override
public void add(FileSystemItem item) {
children.add(item);
}
@Override
public void remove(FileSystemItem item) {
children.remove(item);
}
@Override
public long getSize() {
// 文件夹大小 = 所有子项大小之和
return children.stream().mapToLong(FileSystemItem::getSize).sum();
}
@Override
public void display(int indent) {
System.out.println(" ".repeat(indent) + "📁 " + name +
" (共 " + getSize() + " KB)");
// 递归显示子项
for (FileSystemItem child : children) {
child.display(indent + 4);
}
}
}
// 测试:构建文件系统树
public class Main {
public static void main(String[] args) {
// 构建文件系统结构
Folder root = new Folder("我的文档");
// 根目录下的文件
root.add(new File("简历.docx", 50));
root.add(new File("合同.pdf", 120));
// 子文件夹:项目
Folder projects = new Folder("项目");
projects.add(new File("需求文档.md", 30));
projects.add(new File("设计图.png", 500));
// 项目下的子文件夹
Folder codeFolder = new Folder("代码");
codeFolder.add(new File("Main.java", 10));
codeFolder.add(new File("Service.java", 25));
codeFolder.add(new File("Dao.java", 20));
projects.add(codeFolder);
root.add(projects);
// 另一个子文件夹:图片
Folder photos = new Folder("图片");
photos.add(new File("旅游照片.jpg", 3000));
photos.add(new File("证件照.jpg", 200));
root.add(photos);
// 显示整个目录树
root.display(0);
System.out.println("\n总大小: " + root.getSize() + " KB");
}
}
输出:
📁 我的文档 (共 3955 KB)
📄 简历.docx (50 KB)
📄 合同.pdf (120 KB)
📁 项目 (共 585 KB)
📄 需求文档.md (30 KB)
📄 设计图.png (500 KB)
📁 代码 (共 55 KB)
📄 Main.java (10 KB)
📄 Service.java (25 KB)
📄 Dao.java (20 KB)
📁 图片 (共 3200 KB)
📄 旅游照片.jpg (3000 KB)
📄 证件照.jpg (200 KB)
总大小: 3955 KB
4.4 装饰器模式(Decorator Pattern)
🍕 生活例子
你点了一杯普通咖啡,然后可以加牛奶(+3元),加糖(+1元),加奶泡(+5元)......
每次"装饰"都给咖啡增加了新功能(味道、价格),但基础咖啡本身没变。
装饰器模式:动态地给对象添加额外的职责,比继承更灵活。
📋 使用场景
- Java I/O 流(BufferedReader、GZIPInputStream 等)
- Spring Web MVC 的 HttpServletRequestWrapper
- 权限校验、日志记录、缓存等横切关注点
💻 代码实现
// 组件接口
public interface Coffee {
String getDescription();
double getCost();
}
// 基础咖啡(被装饰的原始对象)
public class SimpleCoffee implements Coffee {
@Override
public String getDescription() { return "简单咖啡"; }
@Override
public double getCost() { return 10.0; }
}
public class Espresso implements Coffee {
@Override
public String getDescription() { return "浓缩咖啡"; }
@Override
public double getCost() { return 15.0; }
}
// 抽象装饰器(实现同一接口,持有被装饰对象的引用)
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
}
// 具体装饰器:加牛奶
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", 加牛奶";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 3.0;
}
}
// 具体装饰器:加糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", 加糖";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 1.0;
}
}
// 具体装饰器:加奶泡
public class WhipDecorator extends CoffeeDecorator {
public WhipDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", 加奶泡";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 5.0;
}
}
// 测试:灵活地给咖啡叠加装饰!
public class CoffeeShop {
public static void main(String[] args) {
// 普通咖啡
Coffee coffee1 = new SimpleCoffee();
System.out.printf("%-30s ¥%.1f%n", coffee1.getDescription(), coffee1.getCost());
// 拿铁:咖啡 + 牛奶
Coffee latte = new MilkDecorator(new SimpleCoffee());
System.out.printf("%-30s ¥%.1f%n", latte.getDescription(), latte.getCost());
// 甜蜜拿铁:咖啡 + 牛奶 + 糖
Coffee sweetLatte = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
System.out.printf("%-30s ¥%.1f%n", sweetLatte.getDescription(), sweetLatte.getCost());
// 豪华咖啡:浓缩 + 牛奶 + 奶泡 + 糖
Coffee luxuryCoffee = new SugarDecorator(
new WhipDecorator(
new MilkDecorator(
new Espresso())));
System.out.printf("%-30s ¥%.1f%n", luxuryCoffee.getDescription(), luxuryCoffee.getCost());
}
}
输出:
简单咖啡 ¥10.0
简单咖啡, 加牛奶 ¥13.0
简单咖啡, 加牛奶, 加糖 ¥14.0
浓缩咖啡, 加牛奶, 加奶泡, 加糖 ¥24.0
🔍 Java I/O 中的装饰器模式
// Java I/O 是装饰器模式的经典实现!
// 基础流(原始对象)
InputStream fileInput = new FileInputStream("data.txt");
// 一层一层包装(装饰)
InputStream buffered = new BufferedInputStream(fileInput); // 加缓冲
InputStream gzip = new GZIPInputStream(buffered); // 加解压
DataInputStream data = new DataInputStream(gzip); // 加数据类型读取
// 使用
int value = data.readInt();
String line = new BufferedReader(new InputStreamReader(data)).readLine();
4.5 外观模式(Facade Pattern)
🍕 生活例子
你去饭店吃饭,只需要跟服务员说"我要一份红烧肉"。你不需要知道厨师怎么烧、食材从哪里采购、厨房里的流程。
服务员就是外观(Facade),屏蔽了内部复杂的系统。
外观模式:为子系统中的一组接口提供一个一致的高层接口,让子系统更容易使用。
📋 使用场景
- 减少客户端与复杂子系统之间的依赖
- 为子系统提供简单的接口(就像 API 网关)
- 分层设计中的层与层之间的接口
💻 代码实现
场景:家庭影院系统,需要操作音响、屏幕、灯光、播放器
// 子系统1:投影屏幕
public class Screen {
public void down() {
System.out.println("🖥️ 投影屏幕:缓缓放下");
}
public void up() {
System.out.println("🖥️ 投影屏幕:缓缓收起");
}
}
// 子系统2:音响系统
public class SoundSystem {
public void on() {
System.out.println("🔊 音响:开机");
}
public void setVolume(int volume) {
System.out.println("🔊 音响:音量调到 " + volume);
}
public void setSurroundMode() {
System.out.println("🔊 音响:切换到环绕立体声模式");
}
public void off() {
System.out.println("🔊 音响:关机");
}
}
// 子系统3:灯光控制
public class Lights {
public void dim(int level) {
System.out.println("💡 灯光:调暗到 " + level + "%");
}
public void on() {
System.out.println("💡 灯光:全亮");
}
}
// 子系统4:蓝光播放器
public class BluRayPlayer {
public void on() {
System.out.println("📀 蓝光播放器:开机");
}
public void play(String movie) {
System.out.println("📀 蓝光播放器:播放《" + movie + "》");
}
public void stop() {
System.out.println("📀 蓝光播放器:停止");
}
public void off() {
System.out.println("📀 蓝光播放器:关机");
}
}
// 子系统5:空调
public class AirConditioner {
public void setTemperature(int temp) {
System.out.println("❄️ 空调:温度设为 " + temp + "°C");
}
public void off() {
System.out.println("❄️ 空调:关机");
}
}
// 外观类:家庭影院外观
public class HomeTheaterFacade {
private Screen screen;
private SoundSystem sound;
private Lights lights;
private BluRayPlayer player;
private AirConditioner ac;
public HomeTheaterFacade() {
this.screen = new Screen();
this.sound = new SoundSystem();
this.lights = new Lights();
this.player = new BluRayPlayer();
this.ac = new AirConditioner();
}
// 外观方法:一键开始看电影
public void watchMovie(String movie) {
System.out.println("=== 🎬 准备观看《" + movie + "》===");
lights.dim(10);
screen.down();
ac.setTemperature(22);
sound.on();
sound.setSurroundMode();
sound.setVolume(70);
player.on();
player.play(movie);
System.out.println("=== 🍿 电影开始,享受吧!===\n");
}
// 外观方法:一键结束看电影
public void endMovie() {
System.out.println("=== 电影结束,收拾一下 ===");
player.stop();
player.off();
sound.off();
screen.up();
lights.on();
ac.off();
System.out.println("=== 晚安!===");
}
}
// 测试:使用外观,简单极了!
public class Main {
public static void main(String[] args) {
HomeTheaterFacade theater = new HomeTheaterFacade();
// 一键开始,不需要知道内部细节!
theater.watchMovie("阿凡达:水之道");
// 看完了
theater.endMovie();
}
}
输出:
=== 🎬 准备观看《阿凡达:水之道》===
💡 灯光:调暗到 10%
🖥️ 投影屏幕:缓缓放下
❄️ 空调:温度设为 22°C
🔊 音响:开机
🔊 音响:切换到环绕立体声模式
🔊 音响:音量调到 70
📀 蓝光播放器:开机
📀 蓝光播放器:播放《阿凡达:水之道》
=== 🍿 电影开始,享受吧!===
...
=== 电影结束,收拾一下 ===
...
=== 晚安!===
4.6 享元模式(Flyweight Pattern)
🍕 生活例子
你在玩一个森林游戏,地图上有10万棵树。如果每棵树都单独存储颜色、纹理等信息,内存会爆炸。
但实际上,大多数树是相同种类的,共享同样的颜色和纹理。只有位置不同。
享元模式:通过共享来高效支持大量细粒度的对象(状态分为内部状态/共享 和 外部状态/不共享)。
💻 代码实现
// 享元对象:树的类型(共享的内部状态)
public class TreeType {
private final String name; // 树种名称(共享)
private final String color; // 颜色(共享)
private final String texture; // 纹理(共享)
public TreeType(String name, String color, String texture) {
this.name = name;
this.color = color;
this.texture = texture;
System.out.println("创建树类型: " + name + " (这个对象会被共享)");
}
// 绘制树(位置是外部状态,不存储在享元中)
public void draw(int x, int y) {
System.out.printf("在(%d,%d)画了一棵%s(%s色)%n", x, y, name, color);
}
}
// 享元工厂:负责管理和复用享元对象
public class TreeTypeFactory {
// 缓存已创建的享元对象
private static Map<String, TreeType> treeTypes = new HashMap<>();
public static TreeType getTreeType(String name, String color, String texture) {
// 生成唯一key
String key = name + "_" + color + "_" + texture;
// 如果已存在,直接返回(复用)
if (!treeTypes.containsKey(key)) {
treeTypes.put(key, new TreeType(name, color, texture));
}
return treeTypes.get(key);
}
public static int getTreeTypeCount() {
return treeTypes.size();
}
}
// 树(非共享部分:位置,是外部状态)
public class Tree {
private int x, y; // 位置(外部状态,每棵树不同)
private TreeType type; // 树类型(内部状态,多棵树共享)
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void draw() {
type.draw(x, y);
}
}
// 森林(管理大量树对象)
public class Forest {
private List<Tree> trees = new ArrayList<>();
public void plantTree(int x, int y, String name, String color, String texture) {
// 获取共享的树类型(如果存在就复用)
TreeType type = TreeTypeFactory.getTreeType(name, color, texture);
trees.add(new Tree(x, y, type));
}
public void draw() {
for (Tree tree : trees) {
tree.draw();
}
}
public int getTreeCount() { return trees.size(); }
}
// 测试:种10棵树,但只有3种树类型对象
public class Main {
public static void main(String[] args) {
Forest forest = new Forest();
// 种10棵树
forest.plantTree(1, 2, "松树", "深绿", "松树纹理");
forest.plantTree(5, 8, "松树", "深绿", "松树纹理"); // 复用!
forest.plantTree(3, 6, "柳树", "浅绿", "柳树纹理");
forest.plantTree(7, 1, "松树", "深绿", "松树纹理"); // 复用!
forest.plantTree(2, 9, "桃树", "粉红", "桃树纹理");
forest.plantTree(8, 4, "柳树", "浅绿", "柳树纹理"); // 复用!
forest.plantTree(4, 7, "松树", "深绿", "松树纹理"); // 复用!
forest.plantTree(6, 3, "桃树", "粉红", "桃树纹理"); // 复用!
forest.plantTree(9, 5, "柳树", "浅绿", "柳树纹理"); // 复用!
forest.plantTree(0, 0, "松树", "深绿", "松树纹理"); // 复用!
System.out.println("\n--- 绘制森林 ---");
forest.draw();
System.out.println("\n--- 统计 ---");
System.out.println("总树数量: " + forest.getTreeCount());
System.out.println("树类型对象数量: " + TreeTypeFactory.getTreeTypeCount()); // 只有3个!
System.out.println("节省了 " + (forest.getTreeCount() - TreeTypeFactory.getTreeTypeCount())
+ " 个重量级对象的内存");
}
}
输出:
创建树类型: 松树 (这个对象会被共享)
创建树类型: 柳树 (这个对象会被共享)
创建树类型: 桃树 (这个对象会被共享)
--- 绘制森林 ---
在(1,2)画了一棵松树(深绿色)
在(5,8)画了一棵松树(深绿色)
...
--- 统计 ---
总树数量: 10
树类型对象数量: 3
节省了 7 个重量级对象的内存
🔍 JDK 中的享元模式
// Integer.valueOf() 缓存了 -128 到 127 的对象(享元!)
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b); // true,是同一个对象(共享)
Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
System.out.println(c == d); // false,超出缓存范围,是不同对象
// String.intern() --- 将字符串加入字符串常量池(享元)
String s1 = new String("hello").intern();
String s2 = "hello";
System.out.println(s1 == s2); // true
4.7 代理模式(Proxy Pattern)
🍕 生活例子
你想见某个大老板,但大老板很忙,你需要先通过他的秘书(代理)预约。秘书可以过滤一些不重要的事情,记录见面时间,等等。
代理模式:为对象提供一个代理,控制对这个对象的访问。
📋 使用场景
- 远程代理:为远程对象提供本地代表(RMI)
- 虚拟代理:延迟创建开销大的对象
- 保护代理:控制访问权限
- 缓存代理:缓存请求结果
- Spring AOP:底层就是动态代理
💻 静态代理
// 主题接口
public interface UserService {
User getUser(int id);
void saveUser(User user);
}
// 真实对象
public class UserServiceImpl implements UserService {
@Override
public User getUser(int id) {
System.out.println("从数据库查询用户: id=" + id);
return new User(id, "张三");
}
@Override
public void saveUser(User user) {
System.out.println("保存用户到数据库: " + user.getName());
}
}
// 静态代理:日志代理
public class UserServiceLogProxy implements UserService {
private UserService target; // 持有真实对象
public UserServiceLogProxy(UserService target) {
this.target = target;
}
@Override
public User getUser(int id) {
System.out.println("[LOG] 开始执行 getUser,参数: id=" + id);
long start = System.currentTimeMillis();
User user = target.getUser(id); // 调用真实方法
long end = System.currentTimeMillis();
System.out.println("[LOG] getUser 执行完毕,耗时: " + (end - start) + "ms");
return user;
}
@Override
public void saveUser(User user) {
System.out.println("[LOG] 开始执行 saveUser,参数: " + user.getName());
long start = System.currentTimeMillis();
target.saveUser(user);
long end = System.currentTimeMillis();
System.out.println("[LOG] saveUser 执行完毕,耗时: " + (end - start) + "ms");
}
}
💻 动态代理(JDK)
import java.lang.reflect.*;
// 通用的日志处理器(动态代理的核心)
public class LoggingHandler implements InvocationHandler {
private Object target;
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[动态代理] 方法 " + method.getName() + " 开始执行");
System.out.println("[动态代理] 参数: " + Arrays.toString(args));
long start = System.currentTimeMillis();
Object result = method.invoke(target, args); // 调用真实方法
long end = System.currentTimeMillis();
System.out.println("[动态代理] 方法 " + method.getName() + " 执行完毕,耗时: " + (end-start) + "ms");
System.out.println("[动态代理] 返回值: " + result);
return result;
}
// 工厂方法:创建代理对象
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 被代理的接口
new LoggingHandler(target) // 处理器
);
}
}
// 测试动态代理
public class Main {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
// 动态创建代理,不需要手动写代理类!
UserService proxy = LoggingHandler.createProxy(realService);
// 使用代理(和使用真实对象完全一样)
User user = proxy.getUser(1);
proxy.saveUser(new User(2, "李四"));
}
}
💻 CGLIB 代理(Spring 用于没有接口的类)
// Spring Boot 项目中,引入 spring-core 即可使用 CGLIB
import org.springframework.cglib.proxy.*;
// 不实现任何接口的类
public class OrderService {
public void createOrder(String productId) {
System.out.println("创建订单: " + productId);
}
}
// CGLIB 代理(通过继承,生成子类来代理)
public class CglibLoggingProxy implements MethodInterceptor {
private Object target;
public CglibLoggingProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("[CGLIB] 前置处理: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("[CGLIB] 后置处理: " + method.getName());
return result;
}
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibLoggingProxy(target));
return (T) enhancer.create();
}
}
// 使用
OrderService proxy = CglibLoggingProxy.createProxy(new OrderService());
proxy.createOrder("PROD-001");
JDK代理 vs CGLIB代理
| JDK 动态代理 | CGLIB 代理 | |
|---|---|---|
| 要求 | 必须有接口 | 无需接口,但类不能是 final |
| 原理 | 实现接口 | 继承(生成子类) |
| 性能 | 较低(反射) | 较高(字节码) |
| Spring 选择 | 有接口时用 | 没接口时用 |
5. 行为型模式
行为型模式关注对象之间的通信和职责分配,让对象之间的交互更加灵活。
5.1 责任链模式(Chain of Responsibility Pattern)
🍕 生活例子
你在公司申请报销,金额不同,需要不同级别的审批:
- 1000元以下:组长审批
- 1000-5000元:经理审批
- 5000-20000元:总监审批
- 20000元以上:CEO审批
申请沿着链向上传递,直到有人能处理为止。
责任链模式:将多个处理者连成链,请求沿链传递,直到有处理者处理。
📋 使用场景
- OA 审批流程
- Spring Security 过滤器链
- Netty Pipeline
- Servlet 过滤器
💻 代码实现
// 审批处理者抽象类
public abstract class Approver {
protected String name;
protected Approver nextApprover; // 下一个处理者
public Approver(String name) {
this.name = name;
}
// 设置下一个处理者(链式构建)
public Approver setNext(Approver next) {
this.nextApprover = next;
return next; // 返回next便于链式调用
}
// 抽象处理方法
public abstract void handle(ExpenseRequest request);
// 传递给下一个处理者
protected void passToNext(ExpenseRequest request) {
if (nextApprover != null) {
nextApprover.handle(request);
} else {
System.out.println("❌ 报销申请 [" + request.getDescription() +
"] 金额: ¥" + request.getAmount() + " 无人审批,退回");
}
}
}
// 报销申请
public class ExpenseRequest {
private String description;
private double amount;
private String applicant;
public ExpenseRequest(String description, double amount, String applicant) {
this.description = description;
this.amount = amount;
this.applicant = applicant;
}
public String getDescription() { return description; }
public double getAmount() { return amount; }
public String getApplicant() { return applicant; }
}
// 具体处理者:组长(审批1000以下)
public class TeamLeader extends Approver {
public TeamLeader(String name) { super(name); }
@Override
public void handle(ExpenseRequest request) {
if (request.getAmount() <= 1000) {
System.out.printf("✅ 组长[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
name, request.getApplicant(), request.getDescription(), request.getAmount());
} else {
System.out.printf("⬆️ 组长[%s]: ¥%.2f 超出我的权限,转交经理%n", name, request.getAmount());
passToNext(request);
}
}
}
// 具体处理者:经理(审批5000以下)
public class Manager extends Approver {
public Manager(String name) { super(name); }
@Override
public void handle(ExpenseRequest request) {
if (request.getAmount() <= 5000) {
System.out.printf("✅ 经理[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
name, request.getApplicant(), request.getDescription(), request.getAmount());
} else {
System.out.printf("⬆️ 经理[%s]: ¥%.2f 超出我的权限,转交总监%n", name, request.getAmount());
passToNext(request);
}
}
}
// 具体处理者:总监(审批20000以下)
public class Director extends Approver {
public Director(String name) { super(name); }
@Override
public void handle(ExpenseRequest request) {
if (request.getAmount() <= 20000) {
System.out.printf("✅ 总监[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
name, request.getApplicant(), request.getDescription(), request.getAmount());
} else {
System.out.printf("⬆️ 总监[%s]: ¥%.2f 超出我的权限,转交CEO%n", name, request.getAmount());
passToNext(request);
}
}
}
// 具体处理者:CEO(审批所有)
public class CEO extends Approver {
public CEO(String name) { super(name); }
@Override
public void handle(ExpenseRequest request) {
System.out.printf("✅ CEO[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
name, request.getApplicant(), request.getDescription(), request.getAmount());
}
}
// 测试:构建审批链
public class Main {
public static void main(String[] args) {
// 构建责任链:组长 → 经理 → 总监 → CEO
Approver teamLeader = new TeamLeader("王组长");
Approver manager = new Manager("李经理");
Approver director = new Director("赵总监");
Approver ceo = new CEO("张总");
// 链式设置下一个处理者
teamLeader.setNext(manager).setNext(director).setNext(ceo);
// 提交不同金额的申请
System.out.println("=== 提交报销申请 ===");
teamLeader.handle(new ExpenseRequest("购买办公用品", 500, "小明"));
System.out.println();
teamLeader.handle(new ExpenseRequest("团队建设活动", 3000, "小红"));
System.out.println();
teamLeader.handle(new ExpenseRequest("采购服务器", 15000, "小张"));
System.out.println();
teamLeader.handle(new ExpenseRequest("收购竞争对手", 100000, "小李"));
}
}
输出:
=== 提交报销申请 ===
✅ 组长[王组长]审批了[小明]的报销申请: 购买办公用品,金额: ¥500.00
⬆️ 组长[王组长]: ¥3000.00 超出我的权限,转交经理
✅ 经理[李经理]审批了[小红]的报销申请: 团队建设活动,金额: ¥3000.00
⬆️ 组长[王组长]: ¥15000.00 超出我的权限,转交经理
⬆️ 经理[李经理]: ¥15000.00 超出我的权限,转交总监
✅ 总监[赵总监]审批了[小张]的报销申请: 采购服务器,金额: ¥15000.00
⬆️ (一路传递到CEO...)
✅ CEO[张总]审批了[小李]的报销申请: 收购竞争对手,金额: ¥100000.00
5.2 命令模式(Command Pattern)
🍕 生活例子
你用遥控器控制电视:按"开机"按钮、按"调高音量"按钮、按"换台"按钮......
每个按钮背后对应一个"命令"。命令可以被记录(实现撤销功能),可以被排队执行,可以被重新执行。
命令模式:将请求封装成对象,从而可以用不同的请求来参数化客户端,也可以队列或记录请求,以及支持撤销操作。
📋 使用场景
- 撤销/重做功能(Ctrl+Z / Ctrl+Y)
- 事务管理(一组命令要么全成功,要么全撤销)
- 操作日志(把命令序列化存储)
- 消息队列(命令序列化后发给消费者)
💻 代码实现
// 命令接口
public interface Command {
void execute();
void undo(); // 撤销
}
// 接收者:文本编辑器
public class TextEditor {
private StringBuilder text = new StringBuilder();
public void insertText(String content, int position) {
text.insert(position, content);
System.out.println("插入后: [" + text.toString() + "]");
}
public void deleteText(int from, int to) {
text.delete(from, to);
System.out.println("删除后: [" + text.toString() + "]");
}
public String getText() { return text.toString(); }
public void setText(String t) { text = new StringBuilder(t); }
}
// 具体命令:插入文本
public class InsertTextCommand implements Command {
private TextEditor editor;
private String content;
private int position;
public InsertTextCommand(TextEditor editor, String content, int position) {
this.editor = editor;
this.content = content;
this.position = position;
}
@Override
public void execute() {
System.out.print("执行: 在位置" + position + "插入[" + content + "] → ");
editor.insertText(content, position);
}
@Override
public void undo() {
System.out.print("撤销: 删除位置" + position + "的[" + content + "] → ");
editor.deleteText(position, position + content.length());
}
}
// 具体命令:删除文本
public class DeleteTextCommand implements Command {
private TextEditor editor;
private int from, to;
private String deletedContent; // 保存被删除的内容,用于撤销
public DeleteTextCommand(TextEditor editor, int from, int to) {
this.editor = editor;
this.from = from;
this.to = to;
}
@Override
public void execute() {
deletedContent = editor.getText().substring(from, to);
System.out.print("执行: 删除位置" + from + "-" + to + " → ");
editor.deleteText(from, to);
}
@Override
public void undo() {
System.out.print("撤销: 恢复[" + deletedContent + "] → ");
editor.insertText(deletedContent, from);
}
}
// 调用者:命令历史管理器(支持撤销/重做)
public class CommandHistory {
private Deque<Command> history = new ArrayDeque<>();
private Deque<Command> redoStack = new ArrayDeque<>();
public void execute(Command command) {
command.execute();
history.push(command);
redoStack.clear(); // 执行新命令后清空重做栈
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
System.out.print("↩️ ");
command.undo();
redoStack.push(command);
} else {
System.out.println("没有可撤销的操作");
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command command = redoStack.pop();
System.out.print("↪️ ");
command.execute();
history.push(command);
} else {
System.out.println("没有可重做的操作");
}
}
}
// 测试
public class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
CommandHistory history = new CommandHistory();
// 执行一系列操作
history.execute(new InsertTextCommand(editor, "Hello", 0));
history.execute(new InsertTextCommand(editor, " World", 5));
history.execute(new InsertTextCommand(editor, "!", 11));
System.out.println("\n当前文本: " + editor.getText());
System.out.println();
// 撤销两次
history.undo();
history.undo();
System.out.println("撤销后: " + editor.getText());
System.out.println();
// 重做一次
history.redo();
System.out.println("重做后: " + editor.getText());
}
}
5.3 迭代器模式(Iterator Pattern)
🍕 生活例子
你有一个装满东西的书包,要一样一样地取出来检查。迭代器就像一只手,帮你一个一个地取,你不用知道书包内部是怎么存放的。
迭代器模式:提供一种方法顺序访问集合对象中的元素,而不暴露集合的内部结构。
📋 使用场景
- Java
Iterator、Iterable接口 - 增强for循环(for-each)底层就是迭代器
- 遍历各种数据结构(数组、链表、树、图)
💻 代码实现
// Java 内置的 Iterator 接口
public interface Iterator<T> {
boolean hasNext();
T next();
}
// 自定义集合
public class NumberCollection implements Iterable<Integer> {
private int[] numbers;
private int size;
public NumberCollection(int capacity) {
numbers = new int[capacity];
size = 0;
}
public void add(int number) {
if (size < numbers.length) {
numbers[size++] = number;
}
}
// 正向迭代器
@Override
public java.util.Iterator<Integer> iterator() {
return new ForwardIterator();
}
// 反向迭代器
public java.util.Iterator<Integer> reverseIterator() {
return new ReverseIterator();
}
// 内部类:正向迭代器
private class ForwardIterator implements java.util.Iterator<Integer> {
private int index = 0;
@Override
public boolean hasNext() { return index < size; }
@Override
public Integer next() {
if (!hasNext()) throw new java.util.NoSuchElementException();
return numbers[index++];
}
}
// 内部类:反向迭代器
private class ReverseIterator implements java.util.Iterator<Integer> {
private int index = size - 1;
@Override
public boolean hasNext() { return index >= 0; }
@Override
public Integer next() {
if (!hasNext()) throw new java.util.NoSuchElementException();
return numbers[index--];
}
}
}
// 测试
public class Main {
public static void main(String[] args) {
NumberCollection collection = new NumberCollection(10);
for (int i = 1; i <= 5; i++) collection.add(i * 10);
// 使用 for-each(底层用迭代器)
System.out.print("正向遍历: ");
for (int num : collection) {
System.out.print(num + " ");
}
System.out.print("\n反向遍历: ");
java.util.Iterator<Integer> it = collection.reverseIterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
// 输出: 反向遍历: 50 40 30 20 10
}
}
5.4 中介者模式(Mediator Pattern)
🍕 生活例子
机场的航班调度中心:所有飞机起降都要通过塔台(中介者)协调,飞机们不直接沟通,避免混乱。
如果没有塔台,飞机们需要互相通讯,网状结构会极其复杂。
中介者模式:用中介对象来封装一系列对象之间的交互,使对象们不直接相互引用,降低耦合。
💻 代码实现
// 中介者接口
public interface ChatMediator {
void sendMessage(String message, User sender);
void addUser(User user);
}
// 用户(同事类)
public abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String message);
public abstract void receive(String message, String senderName);
public String getName() { return name; }
}
// 具体用户
public class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String message) {
System.out.println(name + " 发送消息: " + message);
mediator.sendMessage(message, this); // 通过中介者发送
}
@Override
public void receive(String message, String senderName) {
System.out.println(" → " + name + " 收到来自 " + senderName + " 的消息: " + message);
}
}
// 具体中介者:聊天室
public class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
users.add(user);
System.out.println("=== " + user.getName() + " 加入了聊天室 ===");
}
@Override
public void sendMessage(String message, User sender) {
// 广播给除发送者外的所有用户
for (User user : users) {
if (user != sender) {
user.receive(message, sender.getName());
}
}
}
}
// 测试
public class Main {
public static void main(String[] args) {
ChatMediator chatRoom = new ChatRoom();
User alice = new ChatUser(chatRoom, "Alice");
User bob = new ChatUser(chatRoom, "Bob");
User charlie = new ChatUser(chatRoom, "Charlie");
chatRoom.addUser(alice);
chatRoom.addUser(bob);
chatRoom.addUser(charlie);
System.out.println();
alice.send("大家好!");
System.out.println();
bob.send("你好,Alice!");
}
}
输出:
=== Alice 加入了聊天室 ===
=== Bob 加入了聊天室 ===
=== Charlie 加入了聊天室 ===
Alice 发送消息: 大家好!
→ Bob 收到来自 Alice 的消息: 大家好!
→ Charlie 收到来自 Alice 的消息: 大家好!
Bob 发送消息: 你好,Alice!
→ Alice 收到来自 Bob 的消息: 你好,Alice!
→ Charlie 收到来自 Bob 的消息: 你好,Alice!
5.5 备忘录模式(Memento Pattern)
🍕 生活例子
玩游戏时,在打Boss之前先存档(Save),打输了就读档(Load)回到存档时的状态。
备忘录模式:在不破坏封装的前提下,捕获并保存对象的内部状态,以便以后恢复。
💻 代码实现
// 备忘录类(保存状态的快照)
public class GameMemento {
private final int level;
private final int health;
private final int gold;
private final String location;
private final String saveTime;
public GameMemento(int level, int health, int gold, String location) {
this.level = level;
this.health = health;
this.gold = gold;
this.location = location;
this.saveTime = new java.util.Date().toString();
}
// 只有 get 方法,不提供 set(保护快照)
public int getLevel() { return level; }
public int getHealth() { return health; }
public int getGold() { return gold; }
public String getLocation() { return location; }
public String getSaveTime() { return saveTime; }
@Override
public String toString() {
return String.format("存档[Lv%d, HP%d, 金币%d, 位置:%s]", level, health, gold, location);
}
}
// 游戏角色(原发器)
public class GameCharacter {
private String name;
private int level;
private int health;
private int gold;
private String location;
public GameCharacter(String name) {
this.name = name;
this.level = 1;
this.health = 100;
this.gold = 0;
this.location = "新手村";
}
// 保存状态(创建备忘录)
public GameMemento save() {
System.out.println("💾 " + name + " 存档: " + this);
return new GameMemento(level, health, gold, location);
}
// 恢复状态(从备忘录恢复)
public void restore(GameMemento memento) {
this.level = memento.getLevel();
this.health = memento.getHealth();
this.gold = memento.getGold();
this.location = memento.getLocation();
System.out.println("📂 " + name + " 读档: " + this);
}
// 游戏事件
public void gainExp() {
level++;
System.out.println("⬆️ " + name + " 升级! → 当前: " + this);
}
public void takeDamage(int damage) {
health -= damage;
System.out.println("💥 " + name + " 受到" + damage + "点伤害 → 当前: " + this);
}
public void earnGold(int amount) {
gold += amount;
}
public void moveTo(String place) {
location = place;
System.out.println("🚶 " + name + " 移动到 " + place);
}
public boolean isDead() { return health <= 0; }
@Override
public String toString() {
return String.format("[Lv%d, HP%d, 金币%d, 位置:%s]", level, health, gold, location);
}
}
// 存档管理器(负责人)
public class SaveManager {
private Map<String, GameMemento> saves = new LinkedHashMap<>();
public void save(String slotName, GameMemento memento) {
saves.put(slotName, memento);
System.out.println(" ✅ 保存到存档槽[" + slotName + "]");
}
public GameMemento load(String slotName) {
GameMemento memento = saves.get(slotName);
if (memento == null) throw new RuntimeException("存档槽[" + slotName + "]不存在");
System.out.println(" 📂 从存档槽[" + slotName + "]加载: " + memento);
return memento;
}
public void listSaves() {
System.out.println("--- 所有存档 ---");
saves.forEach((slot, mem) -> System.out.println(" [" + slot + "]: " + mem));
}
}
// 测试
public class Main {
public static void main(String[] args) {
GameCharacter hero = new GameCharacter("勇者小明");
SaveManager saveManager = new SaveManager();
// 探索新手村
hero.earnGold(100);
hero.gainExp();
// 在进入危险地区前存档
saveManager.save("槽位1", hero.save());
// 进入危险地区
hero.moveTo("暗黑森林");
hero.takeDamage(60);
hero.gainExp();
// 在Boss门口存档
saveManager.save("槽位2", hero.save());
// 挑战Boss,失败了
System.out.println("\n=== 挑战Boss ===");
hero.takeDamage(200); // 被打死了
System.out.println("💀 " + hero.isDead() ? "游戏结束!" : "");
// 读取Boss前的存档
System.out.println("\n=== 读取存档,重新挑战 ===");
hero.restore(saveManager.load("槽位2"));
System.out.println("\n=== 所有存档 ===");
saveManager.listSaves();
}
}
5.6 观察者模式(Observer Pattern)
🍕 生活例子
你关注了某个微信公众号,每当它发布新文章,你就会收到推送通知。公众号是"发布者",你是"订阅者"(观察者)。
观察者模式(发布-订阅模式):定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都自动收到通知。
📋 使用场景
- Spring 事件机制(ApplicationEvent)
- GUI 事件处理(按钮点击等)
- MQ(消息队列)的发布订阅
- Android/iOS 的通知机制
💻 代码实现
// 观察者接口
public interface Observer {
void update(String eventType, Object data);
}
// 被观察者接口(发布者)
public interface Observable {
void addObserver(String eventType, Observer observer);
void removeObserver(String eventType, Observer observer);
void notifyObservers(String eventType, Object data);
}
// 电商订单系统(被观察者)
public class OrderSystem implements Observable {
// 事件类型 → 观察者列表
private Map<String, List<Observer>> observerMap = new HashMap<>();
@Override
public void addObserver(String eventType, Observer observer) {
observerMap.computeIfAbsent(eventType, k -> new ArrayList<>()).add(observer);
}
@Override
public void removeObserver(String eventType, Observer observer) {
List<Observer> observers = observerMap.get(eventType);
if (observers != null) observers.remove(observer);
}
@Override
public void notifyObservers(String eventType, Object data) {
List<Observer> observers = observerMap.getOrDefault(eventType, Collections.emptyList());
for (Observer observer : observers) {
observer.update(eventType, data);
}
}
// 业务方法:创建订单
public void createOrder(Order order) {
System.out.println("📦 订单系统: 订单 " + order.getId() + " 创建成功");
// 通知所有"订单创建"事件的观察者
notifyObservers("ORDER_CREATED", order);
}
// 业务方法:订单支付
public void payOrder(Order order) {
System.out.println("💰 订单系统: 订单 " + order.getId() + " 支付成功");
notifyObservers("ORDER_PAID", order);
}
// 业务方法:订单发货
public void shipOrder(Order order) {
System.out.println("🚚 订单系统: 订单 " + order.getId() + " 已发货");
notifyObservers("ORDER_SHIPPED", order);
}
}
// 简单的订单类
public class Order {
private String id;
private String productName;
private double amount;
private String customerPhone;
private String customerEmail;
public Order(String id, String productName, double amount, String phone, String email) {
this.id = id; this.productName = productName; this.amount = amount;
this.customerPhone = phone; this.customerEmail = email;
}
public String getId() { return id; }
public String getProductName() { return productName; }
public double getAmount() { return amount; }
public String getCustomerPhone() { return customerPhone; }
public String getCustomerEmail() { return customerEmail; }
}
// 观察者1:短信通知服务
public class SmsNotificationObserver implements Observer {
@Override
public void update(String eventType, Object data) {
Order order = (Order) data;
switch (eventType) {
case "ORDER_CREATED":
System.out.println(" 📱 短信: 发送给 " + order.getCustomerPhone() +
" - 您的订单" + order.getId() + "已创建");
break;
case "ORDER_PAID":
System.out.println(" 📱 短信: 发送给 " + order.getCustomerPhone() +
" - 您的订单" + order.getId() + "支付¥" + order.getAmount() + "成功");
break;
case "ORDER_SHIPPED":
System.out.println(" 📱 短信: 发送给 " + order.getCustomerPhone() +
" - 您的订单" + order.getId() + "已发货,请注意查收");
break;
}
}
}
// 观察者2:邮件通知服务
public class EmailNotificationObserver implements Observer {
@Override
public void update(String eventType, Object data) {
Order order = (Order) data;
if ("ORDER_CREATED".equals(eventType)) {
System.out.println(" 📧 邮件: 发送给 " + order.getCustomerEmail() +
" - 订单确认邮件");
}
}
}
// 观察者3:库存服务
public class InventoryObserver implements Observer {
@Override
public void update(String eventType, Object data) {
Order order = (Order) data;
if ("ORDER_PAID".equals(eventType)) {
System.out.println(" 📦 库存: 减少商品[" + order.getProductName() + "]库存1件");
}
}
}
// 观察者4:数据统计服务
public class StatisticsObserver implements Observer {
private double totalRevenue = 0;
@Override
public void update(String eventType, Object data) {
Order order = (Order) data;
if ("ORDER_PAID".equals(eventType)) {
totalRevenue += order.getAmount();
System.out.println(" 📊 统计: 累计营收 ¥" + totalRevenue);
}
}
}
// 测试
public class Main {
public static void main(String[] args) {
OrderSystem orderSystem = new OrderSystem();
// 注册观察者
Observer smsObs = new SmsNotificationObserver();
Observer emailObs = new EmailNotificationObserver();
Observer inventoryObs = new InventoryObserver();
Observer statsObs = new StatisticsObserver();
orderSystem.addObserver("ORDER_CREATED", smsObs);
orderSystem.addObserver("ORDER_CREATED", emailObs);
orderSystem.addObserver("ORDER_PAID", smsObs);
orderSystem.addObserver("ORDER_PAID", inventoryObs);
orderSystem.addObserver("ORDER_PAID", statsObs);
orderSystem.addObserver("ORDER_SHIPPED", smsObs);
// 模拟订单流程
Order order1 = new Order("ORD-001", "iPhone 15", 7999.0, "138xxxx", "user@email.com");
orderSystem.createOrder(order1);
System.out.println();
orderSystem.payOrder(order1);
System.out.println();
orderSystem.shipOrder(order1);
}
}
输出:
📦 订单系统: 订单 ORD-001 创建成功
📱 短信: 发送给 138xxxx - 您的订单ORD-001已创建
📧 邮件: 发送给 user@email.com - 订单确认邮件
💰 订单系统: 订单 ORD-001 支付成功
📱 短信: 发送给 138xxxx - 您的订单ORD-001支付¥7999.0成功
📦 库存: 减少商品[iPhone 15]库存1件
📊 统计: 累计营收 ¥7999.0
🚚 订单系统: 订单 ORD-001 已发货
📱 短信: 发送给 138xxxx - 您的订单ORD-001已发货,请注意查收
🔍 Spring 中的观察者模式
// Spring 事件(观察者模式的高级实现)
// 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() { return order; }
}
// 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// ... 业务逻辑
publisher.publishEvent(new OrderCreatedEvent(this, order)); // 发布事件
}
}
// 订阅事件(观察者)
@Component
public class SmsListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("发送短信: " + event.getOrder().getId());
}
}
@Component
public class EmailListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("发送邮件: " + event.getOrder().getId());
}
}
5.7 状态模式(State Pattern)
🍕 生活例子
自动售货机有几种状态:待机(有货/无货)、已投币、已选择商品、正在出货。
不同状态下,按下同一个按钮会有不同的行为。状态模式让对象在内部状态改变时改变它的行为。
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
💻 代码实现
// 状态接口
public interface VendingMachineState {
void insertCoin(VendingMachine machine);
void ejectCoin(VendingMachine machine);
void selectProduct(VendingMachine machine, String product);
void dispense(VendingMachine machine);
}
// 自动售货机
public class VendingMachine {
private VendingMachineState currentState;
private int coins = 0;
private Map<String, Integer> inventory = new HashMap<>();
// 所有可能的状态
private final VendingMachineState noCoinState = new NoCoinState();
private final VendingMachineState hasCoinState = new HasCoinState();
private final VendingMachineState soldState = new SoldState();
private final VendingMachineState soldOutState = new SoldOutState();
public VendingMachine() {
inventory.put("可乐", 3);
inventory.put("雪碧", 2);
inventory.put("矿泉水", 5);
currentState = noCoinState; // 初始状态:无硬币
}
// 状态转换方法
public void setState(VendingMachineState state) { this.currentState = state; }
public VendingMachineState getNoCoinState() { return noCoinState; }
public VendingMachineState getHasCoinState() { return hasCoinState; }
public VendingMachineState getSoldState() { return soldState; }
public VendingMachineState getSoldOutState() { return soldOutState; }
public void addCoins(int amount) { this.coins += amount; }
public void returnCoins() {
System.out.println("退还硬币: " + coins + "元");
coins = 0;
}
public int getCoins() { return coins; }
public boolean hasProduct(String product) {
return inventory.getOrDefault(product, 0) > 0;
}
public void reduceInventory(String product) {
inventory.merge(product, -1, Integer::sum);
System.out.println("出货: " + product + ",剩余: " + inventory.get(product) + "瓶");
}
public boolean isEmptyInventory() {
return inventory.values().stream().allMatch(v -> v == 0);
}
// 委托给当前状态处理
public void insertCoin() { currentState.insertCoin(this); }
public void ejectCoin() { currentState.ejectCoin(this); }
public void selectProduct(String product) { currentState.selectProduct(this, product); }
public void displayStatus() {
System.out.println("--- 售货机状态 ---");
System.out.println("当前状态: " + currentState.getClass().getSimpleName());
System.out.println("投入金额: " + coins + "元");
System.out.println("库存: " + inventory);
System.out.println();
}
}
// 状态1:无硬币状态
public class NoCoinState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine machine) {
machine.addCoins(1);
System.out.println("✅ 投入1元,当前投入: " + machine.getCoins() + "元");
machine.setState(machine.getHasCoinState());
}
@Override
public void ejectCoin(VendingMachine machine) {
System.out.println("❌ 没有投币,无法退款");
}
@Override
public void selectProduct(VendingMachine machine, String product) {
System.out.println("❌ 请先投币");
}
@Override
public void dispense(VendingMachine machine) {
System.out.println("❌ 请先投币再选商品");
}
}
// 状态2:有硬币状态
public class HasCoinState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine machine) {
machine.addCoins(1);
System.out.println("✅ 再次投入1元,当前投入: " + machine.getCoins() + "元");
}
@Override
public void ejectCoin(VendingMachine machine) {
machine.returnCoins();
machine.setState(machine.getNoCoinState());
}
@Override
public void selectProduct(VendingMachine machine, String product) {
if (!machine.hasProduct(product)) {
System.out.println("❌ " + product + " 已售罄");
return;
}
if (machine.getCoins() < 2) { // 假设所有商品2元
System.out.println("❌ 余额不足(需要2元,当前" + machine.getCoins() + "元)");
return;
}
System.out.println("✅ 已选择: " + product);
machine.setState(machine.getSoldState());
machine.currentState.dispense(machine); // 触发出货
// 实际应该在dispense中操作,这里简化
}
@Override
public void dispense(VendingMachine machine) {
System.out.println("❌ 请先选择商品");
}
}
// 状态3:出货状态
public class SoldState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine machine) {
System.out.println("⏳ 正在出货,请稍候");
}
@Override
public void ejectCoin(VendingMachine machine) {
System.out.println("❌ 已选择商品,无法退币");
}
@Override
public void selectProduct(VendingMachine machine, String product) {
System.out.println("⏳ 正在出货,请稍候");
}
@Override
public void dispense(VendingMachine machine) {
// 实际出货逻辑在HasCoinState.selectProduct中触发
}
}
// 状态4:售罄状态
public class SoldOutState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine machine) {
System.out.println("❌ 商品已售罄,不接受投币");
machine.returnCoins();
}
@Override
public void ejectCoin(VendingMachine machine) { machine.returnCoins(); }
@Override
public void selectProduct(VendingMachine machine, String product) {
System.out.println("❌ 商品已售罄");
}
@Override
public void dispense(VendingMachine machine) {
System.out.println("❌ 商品已售罄");
}
}
5.8 策略模式(Strategy Pattern)
🍕 生活例子
你去旅行,可以选择不同的出行方式:坐飞机(快但贵)、坐火车(适中)、开车(自由但慢)。
每种出行方式就是一种"策略",你可以根据情况切换。
策略模式:定义一系列算法,把它们封装起来,并且使它们可以互相替换。让算法的变化独立于使用算法的客户端。
📋 使用场景
- 购物车的多种促销算法
- 排序算法的选择
- 支付方式的选择
Comparator接口就是策略模式
💻 代码实现
// 策略接口:优惠计算
public interface DiscountStrategy {
double calculateDiscount(double originalPrice, int quantity);
String getDescription();
}
// 策略1:无折扣
public class NoDiscountStrategy implements DiscountStrategy {
@Override
public double calculateDiscount(double originalPrice, int quantity) {
return 0; // 没有折扣
}
@Override
public String getDescription() { return "无折扣"; }
}
// 策略2:百分比折扣(打折)
public class PercentageDiscountStrategy implements DiscountStrategy {
private double discountRate; // 折扣率,如0.2表示八折(减20%)
public PercentageDiscountStrategy(double discountRate) {
this.discountRate = discountRate;
}
@Override
public double calculateDiscount(double originalPrice, int quantity) {
return originalPrice * quantity * discountRate;
}
@Override
public String getDescription() {
return (int)((1 - discountRate) * 10) + "折优惠";
}
}
// 策略3:满减优惠
public class FullReductionStrategy implements DiscountStrategy {
private double threshold; // 满减门槛
private double reduction; // 减多少
public FullReductionStrategy(double threshold, double reduction) {
this.threshold = threshold;
this.reduction = reduction;
}
@Override
public double calculateDiscount(double originalPrice, int quantity) {
double total = originalPrice * quantity;
// 可以叠加:满200减30,满400减60
return Math.floor(total / threshold) * reduction;
}
@Override
public String getDescription() {
return "满" + (int)threshold + "减" + (int)reduction;
}
}
// 策略4:买X送X(第二件半价等)
public class BuyGetFreeStrategy implements DiscountStrategy {
private int buyCount; // 买几件
private int freeCount; // 送几件
public BuyGetFreeStrategy(int buyCount, int freeCount) {
this.buyCount = buyCount;
this.freeCount = freeCount;
}
@Override
public double calculateDiscount(double originalPrice, int quantity) {
// 每 buyCount+freeCount 件中,有 freeCount 件免费
int groups = quantity / (buyCount + freeCount);
int remainBuy = Math.min(quantity % (buyCount + freeCount), buyCount);
int freeItems = groups * freeCount;
return freeItems * originalPrice;
}
@Override
public String getDescription() {
return "买" + buyCount + "送" + freeCount;
}
}
// 购物车(使用策略的环境类)
public class ShoppingCart {
private List<CartItem> items = new ArrayList<>();
private DiscountStrategy discountStrategy;
public ShoppingCart() {
this.discountStrategy = new NoDiscountStrategy(); // 默认无折扣
}
public void setDiscountStrategy(DiscountStrategy strategy) {
this.discountStrategy = strategy;
System.out.println("🎁 当前优惠活动: " + strategy.getDescription());
}
public void addItem(String name, double price, int quantity) {
items.add(new CartItem(name, price, quantity));
}
public void checkout() {
System.out.println("\n=== 购物清单 ===");
double originalTotal = 0;
for (CartItem item : items) {
double subtotal = item.price * item.quantity;
originalTotal += subtotal;
System.out.printf(" %-15s ¥%.2f × %d = ¥%.2f%n",
item.name, item.price, item.quantity, subtotal);
}
System.out.printf("原价合计: ¥%.2f%n", originalTotal);
double discount = discountStrategy.calculateDiscount(
items.isEmpty() ? 0 : items.get(0).price,
items.stream().mapToInt(i -> i.quantity).sum()
);
// 简化:直接对总价应用折扣
double discountAmount = 0;
for (CartItem item : items) {
discountAmount += discountStrategy.calculateDiscount(item.price, item.quantity);
}
double finalPrice = originalTotal - discountAmount;
System.out.printf("优惠金额: -¥%.2f (%s)%n", discountAmount, discountStrategy.getDescription());
System.out.printf("实付金额: ¥%.2f%n", finalPrice);
System.out.println("===============\n");
}
static class CartItem {
String name;
double price;
int quantity;
CartItem(String name, double price, int quantity) {
this.name = name; this.price = price; this.quantity = quantity;
}
}
}
// 测试:灵活切换优惠策略
public class Main {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
cart.addItem("T恤", 99.0, 3);
cart.addItem("裤子", 159.0, 2);
// 双十一活动:八折
cart.setDiscountStrategy(new PercentageDiscountStrategy(0.2)); // 减去20%
cart.checkout();
// 满减活动:满200减30
cart.setDiscountStrategy(new FullReductionStrategy(200, 30));
cart.checkout();
// 买二送一
cart.setDiscountStrategy(new BuyGetFreeStrategy(2, 1));
cart.checkout();
}
}
5.9 模板方法模式(Template Method Pattern)
🍕 生活例子
做菜的步骤:准备食材 → 热锅 → 放油 → 烹饪 → 出锅
这个框架(步骤顺序)是固定的,但每道菜的具体烹饪方式不同。
模板方法模式定义了算法的框架,把具体步骤延迟到子类实现。
模板方法模式:在父类中定义算法的骨架,将某些步骤延迟到子类中实现,使得子类可以在不改变算法结构的情况下重新定义某些步骤。
📋 使用场景
- JDK
AbstractList(实现了大量通用方法,只需子类实现几个抽象方法) - Spring
JdbcTemplate(定义了JDBC操作的模板) - MyBatis
BaseExecutor
💻 代码实现
// 抽象类:定义炒菜的模板
public abstract class CookingTemplate {
// 模板方法(final,不允许子类修改流程)
public final void cook() {
System.out.println("=== 开始烹饪: " + getDishName() + " ===");
prepareIngredients();
heatPan();
addOil();
addIngredients();
stirFry();
addSeasoning();
serve();
System.out.println("=== " + getDishName() + " 大功告成!===\n");
}
// 抽象方法:子类必须实现
protected abstract String getDishName();
protected abstract void prepareIngredients();
protected abstract void addIngredients();
protected abstract void stirFry();
// 具体方法:所有子类共用
private void heatPan() {
System.out.println(" 1. 开火,热锅");
}
private void addOil() {
System.out.println(" 2. 倒入适量食用油");
}
private void serve() {
System.out.println(" 6. 装盘,上桌!");
}
// 钩子方法:子类可以选择性重写
protected void addSeasoning() {
System.out.println(" 5. 加盐、鸡精调味");
}
}
// 具体子类1:番茄炒蛋
public class TomatoAndEggCooking extends CookingTemplate {
@Override
protected String getDishName() { return "番茄炒蛋"; }
@Override
protected void prepareIngredients() {
System.out.println(" 准备食材: 番茄2个切块,鸡蛋3个打散加盐");
}
@Override
protected void addIngredients() {
System.out.println(" 3. 先倒鸡蛋液,炒成块状后盛出备用");
System.out.println(" 再放番茄翻炒出汁");
}
@Override
protected void stirFry() {
System.out.println(" 4. 鸡蛋回锅,与番茄一起翻炒均匀");
}
// 重写钩子方法:番茄炒蛋加糖提鲜
@Override
protected void addSeasoning() {
System.out.println(" 5. 加盐、少许糖(提鲜),翻炒均匀");
}
}
// 具体子类2:青椒肉丝
public class GreenPepperAndMeatCooking extends CookingTemplate {
@Override
protected String getDishName() { return "青椒肉丝"; }
@Override
protected void prepareIngredients() {
System.out.println(" 准备食材: 猪肉200g切丝腌制,青椒3个切丝,大蒜切片");
}
@Override
protected void addIngredients() {
System.out.println(" 3. 先下蒜片爆香,再放肉丝翻炒至变色");
System.out.println(" 加入青椒丝翻炒");
}
@Override
protected void stirFry() {
System.out.println(" 4. 大火翻炒2分钟,青椒断生即可");
}
}
// 测试
public class Main {
public static void main(String[] args) {
// 做番茄炒蛋
CookingTemplate dish1 = new TomatoAndEggCooking();
dish1.cook();
// 做青椒肉丝
CookingTemplate dish2 = new GreenPepperAndMeatCooking();
dish2.cook();
}
}
输出:
=== 开始烹饪: 番茄炒蛋 ===
准备食材: 番茄2个切块,鸡蛋3个打散加盐
1. 开火,热锅
2. 倒入适量食用油
3. 先倒鸡蛋液,炒成块状后盛出备用
再放番茄翻炒出汁
4. 鸡蛋回锅,与番茄一起翻炒均匀
5. 加盐、少许糖(提鲜),翻炒均匀
6. 装盘,上桌!
=== 番茄炒蛋 大功告成!===
5.10 访问者模式(Visitor Pattern)
🍕 生活例子
医院体检:不同的检查医生(访问者)到你这里(元素)进行检查。眼科医生检查眼睛,心内科医生检查心脏,每个医生只专注自己的检查项目。
访问者模式:在不改变已有类的前提下,为这些类添加新的操作。
💻 代码实现
// 访问者接口
public interface Visitor {
void visit(Book book);
void visit(Electronics electronics);
void visit(Clothing clothing);
}
// 元素接口
public interface Product {
void accept(Visitor visitor);
String getName();
double getPrice();
}
// 具体元素:书
public class Book implements Product {
private String name;
private double price;
private String isbn;
public Book(String name, double price, String isbn) {
this.name = name; this.price = price; this.isbn = isbn;
}
@Override
public void accept(Visitor visitor) { visitor.visit(this); }
@Override
public String getName() { return name; }
@Override
public double getPrice() { return price; }
public String getIsbn() { return isbn; }
}
// 具体元素:电子产品
public class Electronics implements Product {
private String name;
private double price;
private int warrantyYears;
public Electronics(String name, double price, int warrantyYears) {
this.name = name; this.price = price; this.warrantyYears = warrantyYears;
}
@Override
public void accept(Visitor visitor) { visitor.visit(this); }
@Override
public String getName() { return name; }
@Override
public double getPrice() { return price; }
public int getWarrantyYears() { return warrantyYears; }
}
// 具体元素:服装
public class Clothing implements Product {
private String name;
private double price;
private String size;
public Clothing(String name, double price, String size) {
this.name = name; this.price = price; this.size = size;
}
@Override
public void accept(Visitor visitor) { visitor.visit(this); }
@Override
public String getName() { return name; }
@Override
public double getPrice() { return price; }
public String getSize() { return size; }
}
// 具体访问者1:折扣计算器
public class DiscountCalculator implements Visitor {
@Override
public void visit(Book book) {
double discount = book.getPrice() * 0.1; // 书打九折
System.out.printf("📚 书籍[%s]: 原价¥%.2f, 享9折, 优惠¥%.2f%n",
book.getName(), book.getPrice(), discount);
}
@Override
public void visit(Electronics electronics) {
double discount = electronics.getPrice() * 0.05; // 电子产品九五折
System.out.printf("💻 电子[%s]: 原价¥%.2f, 享95折, 优惠¥%.2f%n",
electronics.getName(), electronics.getPrice(), discount);
}
@Override
public void visit(Clothing clothing) {
double discount = clothing.getPrice() * 0.2; // 服装打八折
System.out.printf("👕 服装[%s]: 原价¥%.2f, 享8折, 优惠¥%.2f%n",
clothing.getName(), clothing.getPrice(), discount);
}
}
// 具体访问者2:税费计算器
public class TaxCalculator implements Visitor {
@Override
public void visit(Book book) {
// 书籍免税
System.out.printf("📚 书籍[%s]: 免税商品, 税费¥0.00%n", book.getName());
}
@Override
public void visit(Electronics electronics) {
double tax = electronics.getPrice() * 0.13; // 13%增值税
System.out.printf("💻 电子[%s]: 增值税13%%, 税费¥%.2f%n", electronics.getName(), tax);
}
@Override
public void visit(Clothing clothing) {
double tax = clothing.getPrice() * 0.09; // 9%增值税
System.out.printf("👕 服装[%s]: 增值税9%%, 税费¥%.2f%n", clothing.getName(), tax);
}
}
// 测试:同一批商品,用不同访问者计算不同信息
public class Main {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Book("Java设计模式", 89.0, "ISBN-001"),
new Electronics("iPhone 15", 7999.0, 2),
new Clothing("运动T恤", 199.0, "XL"),
new Book("深入理解JVM", 119.0, "ISBN-002")
);
System.out.println("=== 双十一优惠计算 ===");
Visitor discountVisitor = new DiscountCalculator();
for (Product p : products) {
p.accept(discountVisitor);
}
System.out.println("\n=== 税费计算 ===");
Visitor taxVisitor = new TaxCalculator();
for (Product p : products) {
p.accept(taxVisitor);
}
}
}
5.11 解释器模式(Interpreter Pattern)
🍕 生活例子
你教计算器理解 "1 + 2 × 3 - 4" 这个表达式,计算器需要"解释"每个符号的含义并计算出结果。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,使用该表示来解释语言中的句子。
注意:解释器模式在实际项目中用的不多,了解即可。
💻 代码实现
// 抽象表达式
public interface Expression {
int interpret();
}
// 终结符表达式:数字
public class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() { return number; }
@Override
public String toString() { return String.valueOf(number); }
}
// 非终结符表达式:加法
public class AddExpression implements Expression {
private Expression left, right;
public AddExpression(Expression left, Expression right) {
this.left = left; this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
@Override
public String toString() { return "(" + left + " + " + right + ")"; }
}
// 非终结符表达式:减法
public class SubtractExpression implements Expression {
private Expression left, right;
public SubtractExpression(Expression left, Expression right) {
this.left = left; this.right = right;
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
@Override
public String toString() { return "(" + left + " - " + right + ")"; }
}
// 非终结符表达式:乘法
public class MultiplyExpression implements Expression {
private Expression left, right;
public MultiplyExpression(Expression left, Expression right) {
this.left = left; this.right = right;
}
@Override
public int interpret() {
return left.interpret() * right.interpret();
}
@Override
public String toString() { return "(" + left + " × " + right + ")"; }
}
// 测试:手动构建表达式树(实际项目中会用解析器自动构建)
public class Main {
public static void main(String[] args) {
// 计算: 1 + 2 × 3 - 4
// 按优先级构建: 1 + (2 × 3) - 4 = (1 + (2×3)) - 4
Expression one = new NumberExpression(1);
Expression two = new NumberExpression(2);
Expression three = new NumberExpression(3);
Expression four = new NumberExpression(4);
Expression twoTimesThree = new MultiplyExpression(two, three); // 2×3
Expression onePlusTwoTimesThree = new AddExpression(one, twoTimesThree); // 1+(2×3)
Expression result = new SubtractExpression(onePlusTwoTimesThree, four); // (1+(2×3))-4
System.out.println("表达式: " + result);
System.out.println("结果: " + result.interpret());
// 输出: 表达式: ((1 + (2 × 3)) - 4)
// 结果: 3
}
}
6. 设计模式在主流框架中的应用
学完23种设计模式,我们来看看它们在实际框架中是怎么用的。这部分能让你豁然开朗,"哦,原来是这个模式!"
6.1 Spring 框架中的设计模式
工厂模式:BeanFactory / ApplicationContext
// Spring 最核心的思想就是工厂模式
// ApplicationContext 就是一个大型工厂,生产和管理所有 Bean
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// getBean() 就是工厂方法
UserService userService = (UserService) context.getBean("userService");
OrderService orderService = context.getBean(OrderService.class);
// Spring 通过反射创建对象,就像工厂方法一样
// @Component, @Service, @Repository 注解的类会被Spring扫描并注册
单例模式:Spring Bean 默认作用域
// Spring Bean 默认是单例的
@Service
public class UserService {
// 这个类在整个应用中只有一个实例
}
// 也可以配置为原型(每次获取都是新实例)
@Service
@Scope("prototype")
public class PrototypeService {
// 每次从容器获取都是新对象
}
代理模式:Spring AOP
// Spring AOP 底层就是动态代理
@Aspect
@Component
public class LoggingAspect {
// 环绕通知:在方法执行前后插入逻辑
@Around("execution(* com.example.service.*.*(..))")
public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
System.out.println("[AOP] 开始执行: " + methodName);
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 调用真实方法
long end = System.currentTimeMillis();
System.out.println("[AOP] 执行完毕: " + methodName + ", 耗时: " + (end-start) + "ms");
return result;
}
}
// 被AOP增强的Service(不需要写任何日志代码)
@Service
public class OrderService {
public Order createOrder(String productId) {
// 只写业务逻辑,日志由AOP自动添加
return new Order(productId);
}
}
观察者模式:Spring 事件机制
// 已在5.6节展示,Spring的ApplicationEvent就是观察者模式
// 使用 @EventListener 注解,非常优雅
@Component
public class OrderEventListeners {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
// 处理订单创建事件
}
@EventListener
@Async // 异步处理
public void onOrderPaid(OrderPaidEvent event) {
// 异步处理支付事件
}
}
模板方法:JdbcTemplate
// Spring JdbcTemplate 是模板方法模式的经典实现
// 模板(JdbcTemplate)定义了JDBC操作的骨架:获取连接→创建Statement→执行→处理结果→关闭
// 你只需要提供具体的SQL和结果映射
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(int id) {
// 你只需要写SQL和结果映射,JdbcTemplate处理其他一切
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
(rs, rowNum) -> new User(rs.getInt("id"), rs.getString("name")), // 结果映射
id
);
}
public List<User> findAll() {
return jdbcTemplate.query(
"SELECT * FROM users",
(rs, rowNum) -> new User(rs.getInt("id"), rs.getString("name"))
);
}
public int save(User user) {
return jdbcTemplate.update(
"INSERT INTO users(name, email) VALUES(?, ?)",
user.getName(), user.getEmail()
);
}
}
装饰器模式:HttpServletRequestWrapper
// Spring Web 中,HttpServletRequestWrapper 是装饰器模式
// 可以在不修改原始请求的情况下,增强请求功能
public class BodyCachingRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
public BodyCachingRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 把请求体缓存起来(因为流只能读一次)
body = IOUtils.toByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() {
return new CachedBodyServletInputStream(body);
}
// 可以多次读取请求体
public String getBody() {
return new String(body, StandardCharsets.UTF_8);
}
}
// 过滤器中使用
@Component
public class RequestLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
BodyCachingRequestWrapper wrappedRequest =
new BodyCachingRequestWrapper((HttpServletRequest) request);
System.out.println("请求体: " + wrappedRequest.getBody()); // 记录日志
chain.doFilter(wrappedRequest, response); // 传递包装后的请求
}
}
6.2 MyBatis 中的设计模式
建造者模式:SqlSessionFactoryBuilder
// MyBatis 使用建造者模式创建 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
// SqlSession 是工厂方法模式
SqlSession session = factory.openSession();
// Mapper 接口的实现是代理模式(JDK动态代理)
UserMapper userMapper = session.getMapper(UserMapper.class);
// userMapper 是一个代理对象!调用它的方法时,会自动执行对应的SQL
User user = userMapper.findById(1);
代理模式:Mapper 接口
// MyBatis 的核心黑魔法:你只写接口,MyBatis 帮你生成实现类(代理)
// 你只写这个接口
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(@Param("id") int id);
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
int insert(User user);
}
// MyBatis 通过动态代理,自动生成实现类
// 当你调用 userMapper.findById(1) 时
// 实际是代理对象在执行 SQL 并把结果映射成 User 对象
模板方法:BaseExecutor
// MyBatis 的 Executor(SQL执行器)使用模板方法模式
// BaseExecutor 定义了查询的骨架
// SimpleExecutor, ReuseExecutor, BatchExecutor 各自实现具体步骤
// 伪代码示意
abstract class BaseExecutor {
public <E> List<E> query(MappedStatement ms, Object parameter, ...) {
// 模板方法:定义查询流程
if (queryCache != null) {
return queryFromCache(...); // 先查缓存
}
return queryFromDatabase(...); // 缓存没有,查数据库
}
// 抽象方法:留给子类实现
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, ...);
}
class SimpleExecutor extends BaseExecutor {
@Override
protected <E> List<E> doQuery(...) {
// 简单执行:每次都创建新的 Statement
}
}
class ReuseExecutor extends BaseExecutor {
@Override
protected <E> List<E> doQuery(...) {
// 复用 Statement:相同SQL复用PreparedStatement
}
}
6.3 JDK 中的设计模式总结
// ===== 工厂方法 =====
Calendar.getInstance() // 工厂方法,根据Locale返回不同Calendar子类
NumberFormat.getInstance()
DriverManager.getConnection()
ThreadFactory.newThread()
// ===== 单例 =====
Runtime.getRuntime() // Runtime 是单例
System.getSecurityManager()
Desktop.getDesktop()
// ===== 建造者 =====
StringBuilder.append().append().toString()
ProcessBuilder
HttpClient.newBuilder().build() // Java 11+
// ===== 原型 =====
Object.clone()
Arrays.copyOf()
// ===== 适配器 =====
Arrays.asList() // 数组 → List
Collections.enumeration() // Collection → Enumeration
InputStreamReader // 字节流 → 字符流
// ===== 装饰器 =====
BufferedInputStream/OutputStream
DataInputStream/OutputStream
GZIPInputStream/OutputStream
// ===== 代理 =====
java.lang.reflect.Proxy // JDK动态代理
// ===== 迭代器 =====
Iterator/Iterable // 所有集合类
// ===== 观察者 =====
java.util.Observable(已废弃)
EventListener(Swing事件)
PropertyChangeListener
// ===== 策略 =====
Comparator/Comparable // 排序策略
ThreadPoolExecutor的拒绝策略
// ===== 模板方法 =====
AbstractList, AbstractSet // 大量抽象集合类
InputStream.read() // 定义了读取框架
HttpServlet.service() // 定义了请求处理框架
// ===== 命令 =====
Runnable // 命令封装
Callable
Executor.execute(Runnable)
// ===== 责任链 =====
ClassLoader的双亲委派 // 经典责任链
javax.servlet.Filter // Servlet过滤器链
// ===== 享元 =====
Integer.valueOf(-128~127) // 整数缓存
String常量池 // 字符串享元
7. 总结与学习建议
7.1 23种设计模式速查表
| 类别 | 模式 | 核心思想 | 一句话记忆 |
|---|---|---|---|
| 创建型 | 单例 | 全局唯一实例 | 全国只有一个国务院 |
| 工厂方法 | 子类决定创建什么 | 麦当劳后厨做你点的东西 | |
| 抽象工厂 | 创建一族相关产品 | 宜家卖配套的整套家具 | |
| 建造者 | 分步构建复杂对象 | 自定义汉堡,一样一样加料 | |
| 原型 | 克隆现有对象 | 复印机复印文件 | |
| 结构型 | 适配器 | 转换不兼容的接口 | 电源适配器 |
| 桥接 | 分离抽象和实现 | 形状和颜色独立变化 | |
| 组合 | 树形结构统一处理 | 文件系统(文件和文件夹) | |
| 装饰器 | 动态添加功能 | 给咖啡加牛奶加糖 | |
| 外观 | 提供简单接口 | 家庭影院一键开始 | |
| 享元 | 共享细粒度对象 | 游戏中大量相同的树 | |
| 代理 | 控制对象访问 | 明星的经纪人 | |
| 行为型 | 责任链 | 请求沿链传递 | 公司逐级审批 |
| 命令 | 请求封装成对象 | 遥控器按钮 | |
| 迭代器 | 顺序访问集合 | 书包一件一件取东西 | |
| 中介者 | 减少对象间直接通信 | 机场塔台调度 | |
| 备忘录 | 保存和恢复状态 | 游戏存档 | |
| 观察者 | 一对多通知 | 微信公众号推送 | |
| 状态 | 对象随状态改变行为 | 自动售货机状态 | |
| 策略 | 可切换的算法族 | 出行方式选择 | |
| 模板方法 | 固定骨架,延迟步骤 | 炒菜的固定步骤 | |
| 访问者 | 不改类就加操作 | 医院体检各科室医生 | |
| 解释器 | 解释语法规则 | 计算器计算表达式 |
7.2 哪些模式最常用?
日常开发中高频使用(必须掌握):
- 单例模式 - Spring Bean 默认就是单例
- 工厂模式 - Spring IoC 容器的核心
- 代理模式 - Spring AOP、MyBatis Mapper
- 观察者模式 - Spring 事件、消息队列
- 策略模式 - 替代大量 if-else
- 模板方法 - JdbcTemplate、抽象基类
- 装饰器模式 - Java I/O、Filter
- 建造者模式 - 复杂对象构建、Lombok @Builder
- 责任链模式 - 过滤器链、审批流程
了解概念即可(中级):
- 适配器 - 整合老系统时用
- 外观 - API网关设计
- 组合 - 树形结构数据
面试会问,了解就行(高级/少用):
13-23. 剩余模式
7.3 如何避免过度设计?
设计模式不是越多越好!
初学者常见错误:写个Hello World也要用几个设计模式。
判断是否该用设计模式的原则:
❓ 问自己:
1. 这里有没有"变化点"?(会不会扩展、会不会修改)
2. 现在代码有没有问题?(重复代码、难以维护)
3. 用了模式,代码是否真的更清晰?
如果三个都是"没有/否",那就不用模式,保持简单!
记住:最好的代码不是用了最多模式的代码,
而是最清晰、最容易理解的代码。
7.4 学习路线建议
第一阶段(1-2周):掌握基础
├── 理解六大原则(先理解,慢慢内化)
├── 掌握单例、工厂、建造者(日常用得最多)
└── 了解代理模式(理解Spring AOP)
第二阶段(2-4周):深入理解
├── 策略、观察者、装饰器(项目中高频)
├── 责任链、模板方法(框架中大量使用)
└── 读Spring源码,找对应的设计模式
第三阶段(1-2月):灵活运用
├── 在项目中刻意识别和使用设计模式
├── 代码Review时考虑设计模式
└── 读《设计模式》、《Head First设计模式》原著
第四阶段(持续):融会贯通
├── 不刻意使用,自然而然就用了正确的模式
└── 能识别和评价他人代码中的设计决策
7.5 推荐资源
书籍:
- 《Head First 设计模式》(入门,有趣易懂)
- 《设计模式:可复用面向对象软件的基础》(GoF经典,深入)
- 《Effective Java》(Java最佳实践,包含大量设计模式思想)
- 《重构:改善既有代码的设计》(配合设计模式一起看)
网站:
- refactoring.guru(图文并茂,强烈推荐)
- 菜鸟教程设计模式
练习方法:
- 读完一个模式,自己不看代码重新写一遍
- 在 GitHub 上找开源项目,找对应模式的使用
- 看 Spring、MyBatis 源码,标注出使用了哪些模式
7.6 一段代码结束全文
最后,用一个综合案例,把常用的几个模式串起来,让你感受设计模式组合使用的魅力:
/**
* 综合案例:一个简易的电商下单流程
* 用到了:策略模式、观察者模式、责任链模式、单例模式、建造者模式
*/
public class ECommerceDemo {
public static void main(String[] args) {
System.out.println("╔══════════════════════════════════╗");
System.out.println("║ 欢迎使用简易电商系统 ║");
System.out.println("╚══════════════════════════════════╝\n");
// 1. 单例模式:获取配置
ConfigManager config = ConfigManager.getInstance();
// 2. 建造者模式:构建订单
Order order = new Order.Builder()
.orderId("ORD-2024-001")
.productName("MacBook Pro")
.price(14999.0)
.quantity(1)
.customerId("USER-001")
.customerName("张三")
.phone("138xxxxxxxx")
.build();
// 3. 策略模式:选择支付方式
PaymentStrategy paymentStrategy = new AlipayPaymentStrategy();
// 4. 责任链模式:订单审核(风控)
OrderValidator validator = buildValidationChain();
// 5. 观察者模式:注册订单事件监听器
OrderEventBus eventBus = new OrderEventBus();
eventBus.subscribe("ORDER_PLACED", new SmsNotifier());
eventBus.subscribe("ORDER_PLACED", new EmailNotifier());
eventBus.subscribe("ORDER_PAID", new InventoryUpdater());
eventBus.subscribe("ORDER_PAID", new StatisticsUpdater());
// 执行下单流程
System.out.println("【步骤1】提交订单: " + order.getOrderId());
if (validator.validate(order)) {
System.out.println("【步骤2】订单验证通过");
eventBus.publish("ORDER_PLACED", order);
System.out.println("\n【步骤3】执行支付: " + paymentStrategy.getDescription());
boolean paySuccess = paymentStrategy.pay(order.getTotalPrice());
if (paySuccess) {
System.out.println("【步骤4】支付成功 ¥" + order.getTotalPrice());
eventBus.publish("ORDER_PAID", order);
System.out.println("\n✅ 下单完成!请耐心等待收货。");
}
}
}
private static OrderValidator buildValidationChain() {
// 建立验证责任链
OrderValidator stockCheck = new StockCheckValidator();
OrderValidator riskCheck = new RiskControlValidator();
OrderValidator limitCheck = new PurchaseLimitValidator();
stockCheck.setNext(riskCheck).setNext(limitCheck);
return stockCheck;
}
}
写在最后
恭喜你读完了这篇文章!🎉
设计模式是通往高级程序员的必经之路,但不要急于求成。
记住一个学习法则:"先理解,后应用,再灵活"。
- 理解:先搞明白每个模式解决什么问题,不要死记代码
- 应用:在实际项目中尝试使用,哪怕用得不完美也没关系
- 灵活:多了解变化,有时候"正宗"的模式并不是最好的,根据实际情况取舍
祝你在 Java 开发的道路上越走越远!💪
本文约 5 万字,涵盖了 23 种 GoF 设计模式的完整讲解,配有大量代码示例和生活类比。如有疑问,欢迎在评论区交流!
附录A:设计模式综合实战案例
A.1 综合案例:设计一个简易 RPC 框架
这个案例综合运用了多个设计模式,是很好的练习题:
/**
* 简易 RPC 框架
* 使用的设计模式:
* - 代理模式:生成远程调用代理
* - 工厂模式:创建代理对象
* - 单例模式:注册中心
* - 策略模式:负载均衡算法
* - 观察者模式:服务上下线通知
*/
// 1. 服务注册中心(单例模式)
public class ServiceRegistry {
private static volatile ServiceRegistry instance;
private Map<String, List<String>> serviceMap = new ConcurrentHashMap<>();
private List<RegistryObserver> observers = new CopyOnWriteArrayList<>();
private ServiceRegistry() {}
public static ServiceRegistry getInstance() {
if (instance == null) {
synchronized (ServiceRegistry.class) {
if (instance == null) instance = new ServiceRegistry();
}
}
return instance;
}
// 服务注册
public void register(String serviceName, String address) {
serviceMap.computeIfAbsent(serviceName, k -> new CopyOnWriteArrayList<>()).add(address);
System.out.println("📋 注册中心: 服务[" + serviceName + "]注册 " + address);
notifyObservers("REGISTER", serviceName, address);
}
// 服务发现
public List<String> discover(String serviceName) {
return serviceMap.getOrDefault(serviceName, Collections.emptyList());
}
// 观察者模式:监听服务变化
public void addObserver(RegistryObserver observer) { observers.add(observer); }
private void notifyObservers(String event, String serviceName, String address) {
observers.forEach(o -> o.onServiceChange(event, serviceName, address));
}
}
public interface RegistryObserver {
void onServiceChange(String event, String serviceName, String address);
}
// 2. 负载均衡策略(策略模式)
public interface LoadBalancer {
String select(List<String> addresses);
}
public class RoundRobinLoadBalancer implements LoadBalancer {
private AtomicInteger index = new AtomicInteger(0);
@Override
public String select(List<String> addresses) {
if (addresses.isEmpty()) throw new RuntimeException("没有可用服务");
int i = index.getAndIncrement() % addresses.size();
return addresses.get(i);
}
}
public class RandomLoadBalancer implements LoadBalancer {
private Random random = new Random();
@Override
public String select(List<String> addresses) {
if (addresses.isEmpty()) throw new RuntimeException("没有可用服务");
return addresses.get(random.nextInt(addresses.size()));
}
}
// 3. RPC 代理工厂(工厂模式 + 代理模式)
public class RpcProxyFactory {
private ServiceRegistry registry;
private LoadBalancer loadBalancer;
public RpcProxyFactory(ServiceRegistry registry, LoadBalancer loadBalancer) {
this.registry = registry;
this.loadBalancer = loadBalancer;
}
@SuppressWarnings("unchecked")
public <T> T createProxy(Class<T> serviceInterface) {
return (T) Proxy.newProxyInstance(
serviceInterface.getClassLoader(),
new Class[]{serviceInterface},
(proxy, method, args) -> {
String serviceName = serviceInterface.getName();
List<String> addresses = registry.discover(serviceName);
String address = loadBalancer.select(addresses);
System.out.printf("🌐 RPC调用: %s.%s() → %s%n",
serviceInterface.getSimpleName(), method.getName(), address);
// 模拟网络调用(实际应序列化参数,通过网络发送)
return simulateRemoteCall(address, method, args);
}
);
}
private Object simulateRemoteCall(String address, Method method, Object[] args) {
// 实际RPC框架这里会做序列化、网络传输、反序列化
System.out.println(" └─ 模拟发送请求到 " + address);
// 简单模拟返回
if (method.getReturnType() == String.class) return "来自" + address + "的响应";
if (method.getReturnType() == int.class) return 42;
return null;
}
}
// 4. 使用示例
public interface UserService {
String getUser(int id);
int getUserCount();
}
public class RpcDemo {
public static void main(String[] args) {
// 初始化
ServiceRegistry registry = ServiceRegistry.getInstance();
// 注册服务实例(模拟多个服务节点)
registry.register(UserService.class.getName(), "192.168.1.1:8080");
registry.register(UserService.class.getName(), "192.168.1.2:8080");
registry.register(UserService.class.getName(), "192.168.1.3:8080");
// 创建代理(使用轮询负载均衡)
RpcProxyFactory factory = new RpcProxyFactory(registry, new RoundRobinLoadBalancer());
UserService userService = factory.createProxy(UserService.class);
// 透明地调用远程服务(就像调用本地方法一样)
System.out.println("\n--- 开始RPC调用 ---");
for (int i = 0; i < 4; i++) {
String result = userService.getUser(i);
System.out.println("结果: " + result);
}
}
}
A.2 综合案例:设计一个插件化系统
/**
* 插件化系统
* 使用:工厂模式 + 策略模式 + 责任链模式 + 观察者模式
*/
// 插件接口
public interface Plugin {
String getName();
String getVersion();
void onLoad(); // 插件加载时
void onUnload(); // 插件卸载时
void execute(Context context); // 插件执行
}
// 执行上下文(建造者模式)
public class Context {
private final Map<String, Object> data;
private Context(Builder builder) {
this.data = Collections.unmodifiableMap(builder.data);
}
public <T> T get(String key, Class<T> type) {
return type.cast(data.get(key));
}
public static class Builder {
private Map<String, Object> data = new HashMap<>();
public Builder put(String key, Object value) {
data.put(key, value);
return this;
}
public Context build() { return new Context(this); }
}
}
// 插件管理器(单例 + 工厂 + 观察者)
public class PluginManager {
private static PluginManager instance = new PluginManager();
private Map<String, Plugin> plugins = new LinkedHashMap<>();
private List<PluginListener> listeners = new ArrayList<>();
private PluginManager() {}
public static PluginManager getInstance() { return instance; }
public void register(Plugin plugin) {
plugins.put(plugin.getName(), plugin);
plugin.onLoad();
notifyListeners("LOADED", plugin);
System.out.println("✅ 插件[" + plugin.getName() + " v" + plugin.getVersion() + "]已加载");
}
public void unregister(String pluginName) {
Plugin plugin = plugins.remove(pluginName);
if (plugin != null) {
plugin.onUnload();
notifyListeners("UNLOADED", plugin);
System.out.println("🗑️ 插件[" + pluginName + "]已卸载");
}
}
// 执行所有插件(责任链模式)
public void executeAll(Context context) {
System.out.println("--- 执行所有插件 ---");
plugins.values().forEach(p -> {
System.out.println("▶️ 执行插件: " + p.getName());
p.execute(context);
});
}
public void addListener(PluginListener listener) { listeners.add(listener); }
private void notifyListeners(String event, Plugin plugin) {
listeners.forEach(l -> l.onPluginEvent(event, plugin));
}
}
public interface PluginListener {
void onPluginEvent(String event, Plugin plugin);
}
// 具体插件示例:日志插件
public class LoggingPlugin implements Plugin {
@Override
public String getName() { return "logging-plugin"; }
@Override
public String getVersion() { return "1.0.0"; }
@Override
public void onLoad() { System.out.println(" 日志插件初始化"); }
@Override
public void onUnload() { System.out.println(" 日志插件清理"); }
@Override
public void execute(Context context) {
System.out.println(" [LOG] 记录执行日志: " + context.get("action", String.class));
}
}
// 具体插件示例:安全插件
public class SecurityPlugin implements Plugin {
@Override
public String getName() { return "security-plugin"; }
@Override
public String getVersion() { return "2.1.0"; }
@Override
public void onLoad() { System.out.println(" 安全插件初始化,加载安全规则"); }
@Override
public void onUnload() { System.out.println(" 安全插件清理"); }
@Override
public void execute(Context context) {
String user = context.get("user", String.class);
System.out.println(" [SECURITY] 检查用户权限: " + user + " → 通过");
}
}
// 测试
public class PluginSystemDemo {
public static void main(String[] args) {
PluginManager pm = PluginManager.getInstance();
// 添加监听器
pm.addListener((event, plugin) ->
System.out.println(" 🔔 监听到插件事件: " + event + " - " + plugin.getName()));
// 注册插件
pm.register(new SecurityPlugin());
pm.register(new LoggingPlugin());
System.out.println();
// 执行所有插件
Context ctx = new Context.Builder()
.put("user", "admin")
.put("action", "DELETE_USER")
.build();
pm.executeAll(ctx);
System.out.println();
// 卸载插件
pm.unregister("logging-plugin");
}
}
附录B:常见面试题整理
B.1 关于单例模式
Q: 单例模式有几种写法?各有什么优缺点?
A: 主要有5种:
1. 饿汉式:类加载时创建,线程安全,但无论用不用都占内存
2. 懒汉式(不加锁):懒加载,但线程不安全
3. 懒汉式(同步方法):线程安全,但并发性能差
4. 双重检查锁(DCL):懒加载 + 线程安全 + 性能好,需要volatile
5. 静态内部类:最优雅,利用JVM类加载保证线程安全
6. 枚举:最安全,防止反射和序列化攻击
推荐使用:静态内部类(普通情况)或枚举(需要防攻击时)
Q: 如何破坏单例?如何防止?
// 破坏方式1:反射
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s1 = constructor.newInstance(); // 通过反射创建新实例
// 防止反射攻击:在构造器中检查
private Singleton() {
if (instance != null) {
throw new RuntimeException("禁止反射调用!");
}
}
// 破坏方式2:序列化
// 防止序列化破坏:实现 readResolve
private Object readResolve() {
return instance; // 返回已有实例
}
// 最彻底的防止方法:使用枚举(JVM层面保证不能反射和序列化)
B.2 关于工厂模式
Q: 简单工厂、工厂方法、抽象工厂的区别?
简单工厂:
- 一个工厂类
- if-else 或 switch 判断创建哪个
- 新增产品要改工厂类(违反开闭原则)
- 适合:产品少且稳定
工厂方法:
- 每个产品有自己的工厂
- 新增产品只需新增产品类+工厂类
- 遵循开闭原则
- 适合:产品会扩展
抽象工厂:
- 工厂能创建一整族产品
- 保证同族产品的一致性
- 新增产品族容易,新增单个产品类型难
- 适合:多个产品族,产品需要配套使用
B.3 关于代理模式
Q: JDK动态代理和CGLIB代理的区别?Spring如何选择?
/*
JDK动态代理:
- 基于接口(必须有接口)
- 使用 java.lang.reflect.Proxy
- 原理:生成实现相同接口的代理类
- 性能:调用时通过反射,稍慢
CGLIB代理:
- 基于继承(不需要接口,但类不能是final)
- 使用字节码生成技术(ASM)
- 原理:生成目标类的子类
- 性能:调用时不用反射,较快
Spring的选择逻辑(Spring 5.x开始默认用CGLIB):
- 实现了接口:可用JDK或CGLIB(Spring默认CGLIB)
- 没有接口:只能用CGLIB
- proxyTargetClass=true:强制用CGLIB
- @EnableAspectJAutoProxy(proxyTargetClass=false):优先用JDK
注意:Spring Boot 2.x 起默认使用 CGLIB!
*/
B.4 关于观察者模式
Q: 观察者模式和发布订阅模式的区别?
观察者模式:
- 观察者直接注册到被观察者
- 两者之间有直接关联
- 同步执行(被观察者状态变化后,立即通知观察者)
发布订阅模式:
- 发布者和订阅者通过消息中间件(Event Bus/消息队列)解耦
- 两者互不知晓
- 可以异步执行
关系:发布订阅是观察者模式的进化版,更松耦合
Spring的ApplicationEvent是观察者模式(还没到消息队列那种程度)
Kafka、RabbitMQ是发布订阅模式(完全解耦)
B.5 关于策略模式
Q: 策略模式如何消除 if-else?
// ❌ 一堆 if-else(每次新增促销都要改这里)
public double calculatePrice(String type, double price) {
if ("NONE".equals(type)) {
return price;
} else if ("PERCENT_10".equals(type)) {
return price * 0.9;
} else if ("FULL_100_MINUS_10".equals(type)) {
return price >= 100 ? price - 10 : price;
}
// 新增促销类型 → 还要继续加 else if
throw new IllegalArgumentException("未知促销类型");
}
// ✅ 策略模式消除 if-else
public interface PricingStrategy {
double calculate(double price);
}
// 策略注册表
Map<String, PricingStrategy> strategies = new HashMap<>();
strategies.put("NONE", price -> price);
strategies.put("PERCENT_10", price -> price * 0.9);
strategies.put("FULL_100_MINUS_10", price -> price >= 100 ? price - 10 : price);
// 新增促销:只需往map里加一条,原代码不用改!
strategies.put("BUY_2_GET_1_FREE", price -> price * 2 / 3);
// 使用
public double calculatePrice(String type, double price) {
PricingStrategy strategy = strategies.get(type);
if (strategy == null) throw new IllegalArgumentException("未知促销类型: " + type);
return strategy.calculate(price);
}
附录C:设计模式之间的关系图
┌─────────────────────────────────────────────┐
│ 创建对象 │
└──────────────┬──────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
[单例模式] [工厂模式] [建造者]
保证唯一实例 封装创建过程 分步构建对象
/ \
[工厂方法] [抽象工厂]
单个产品 产品族
┌────────────────────────────────────────────────────────────────┐
│ 常见组合使用 │
├────────────────────────────────────────────────────────────────┤
│ 工厂 + 单例:工厂创建的对象是单例(Spring Bean工厂) │
│ 代理 + 策略:代理拦截后选择不同策略执行(AOP + 策略) │
│ 观察者 + 命令:事件系统(事件=命令,订阅者=处理器) │
│ 装饰器 + 责任链:Servlet过滤器链(每个Filter是装饰器+链节点) │
│ 模板方法 + 策略:框架提供模板,具体步骤用策略替换 │
│ 工厂 + 原型:工厂通过克隆原型来创建对象(原型工厂) │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ 容易混淆的模式对比 │
├────────────────────────────────────────────────────────────────┤
│ 代理 vs 装饰器: │
│ 代理:控制访问,代理类自己决定是否/如何调用目标 │
│ 装饰器:增强功能,客户端决定叠加哪些装饰 │
│ │
│ 策略 vs 状态: │
│ 策略:外部传入,客户端决定用哪个算法 │
│ 状态:内部切换,对象自己根据条件切换状态 │
│ │
│ 外观 vs 中介者: │
│ 外观:简化子系统的接口,单向(外部→子系统) │
│ 中介者:协调对象间通信,双向(对象↔中介者↔对象) │
│ │
│ 装饰器 vs 桥接: │
│ 装饰器:动态添加功能,强调"功能叠加" │
│ 桥接:分离两个独立变化的维度,强调"维度正交" │
└────────────────────────────────────────────────────────────────┘
附录D:用 Java 8+ 特性简化设计模式
Java 8 引入了 Lambda 表达式和函数式接口,可以让某些设计模式的代码更简洁:
// ===== 策略模式 + Lambda =====
// 传统方式
new Thread(new Runnable() {
@Override
public void run() { System.out.println("线程执行"); }
}).start();
// Lambda 简化(Runnable本身就是策略接口)
new Thread(() -> System.out.println("线程执行")).start();
// 策略Map
Map<String, Function<Double, Double>> discounts = new HashMap<>();
discounts.put("VIP", price -> price * 0.8);
discounts.put("NORMAL", price -> price * 0.9);
discounts.put("GUEST", price -> price);
double finalPrice = discounts.get("VIP").apply(100.0); // 80.0
// ===== 命令模式 + Lambda =====
// 传统方式(需要接口和实现类)
// 现在直接用 Runnable 或自定义函数接口
List<Runnable> commands = new ArrayList<>();
commands.add(() -> System.out.println("执行命令1"));
commands.add(() -> System.out.println("执行命令2"));
commands.add(() -> System.out.println("执行命令3"));
commands.forEach(Runnable::run);
// ===== 观察者模式 + Lambda =====
// 传统方式需要定义Observer接口和实现类
// Java 8 可以直接用函数式接口
Map<String, List<Consumer<String>>> listeners = new HashMap<>();
listeners.put("click", new ArrayList<>());
listeners.get("click").add(event -> System.out.println("处理点击: " + event));
listeners.get("click").add(event -> System.out.println("记录点击日志: " + event));
// 触发
listeners.get("click").forEach(l -> l.accept("按钮被点击"));
// ===== 模板方法 + Lambda(函数回调)=====
// 传统模板方法需要继承
// Java 8 可以用函数参数代替继承
public class DataProcessor {
// 模板方法:参数化需要变化的步骤
public <T, R> R process(T data,
Function<T, T> preProcess, // 预处理(可变)
Function<T, R> transform, // 转换(可变)
Consumer<R> postProcess) { // 后处理(可变)
T preprocessed = preProcess.apply(data);
R result = transform.apply(preprocessed);
postProcess.accept(result);
return result;
}
}
// 使用
DataProcessor processor = new DataProcessor();
String result = processor.process(
" hello world ",
String::trim, // 预处理:去空格
String::toUpperCase, // 转换:大写
s -> System.out.println("结果: " + s) // 后处理:打印
);
// ===== 建造者模式 + Lambda =====
// 更灵活的建造者
public class FlexibleBuilder<T> {
private T object;
public FlexibleBuilder(Supplier<T> constructor) {
this.object = constructor.get();
}
public FlexibleBuilder<T> with(Consumer<T> setter) {
setter.accept(object);
return this;
}
public T build() { return object; }
}
// 使用(User不需要特殊的Builder方法)
User user = new FlexibleBuilder<>(User::new)
.with(u -> u.setName("张三"))
.with(u -> u.setAge(25))
.with(u -> u.setEmail("zhangsan@example.com"))
.build();
附录E:设计模式反面教材(反模式)
学设计模式,也要知道哪些是不好的做法(反模式):
E.1 贫血模型(Anti-Pattern)
// ❌ 贫血模型:领域对象只有属性,没有行为
public class Order {
private String id;
private double totalPrice;
private String status;
// 只有 getter/setter,没有任何业务逻辑
}
// 业务逻辑全塞在 Service 里(过于臃肿)
public class OrderService {
public void cancelOrder(Order order) {
if (!"PENDING".equals(order.getStatus())) {
throw new RuntimeException("只有待处理订单可以取消");
}
order.setStatus("CANCELLED");
// ...大量逻辑
}
}
// ✅ 充血模型:领域对象包含自己的行为
public class Order {
private String id;
private double totalPrice;
private OrderStatus status;
public void cancel() {
if (status != OrderStatus.PENDING) {
throw new IllegalStateException("只有待处理订单可以取消");
}
this.status = OrderStatus.CANCELLED;
// 领域事件、状态变更逻辑
}
public boolean canShip() {
return status == OrderStatus.PAID;
}
}
E.2 万能类(God Object Anti-Pattern)
// ❌ 万能类:一个类做所有事情(违反单一职责)
public class SystemManager {
// 管理用户
public void createUser() {}
public void deleteUser() {}
// 管理订单
public void createOrder() {}
public void cancelOrder() {}
// 发送邮件
public void sendEmail() {}
// 操作数据库
public void executeSQL() {}
// 管理文件
public void readFile() {}
public void writeFile() {}
// ... 100个方法
// 这种类根本无法维护!
}
// ✅ 分而治之,每个类专注一件事
public class UserService { ... }
public class OrderService { ... }
public class EmailService { ... }
public class DatabaseHelper { ... }
public class FileManager { ... }
E.3 过度设计(Over-Engineering Anti-Pattern)
// ❌ 过度设计:一个简单的配置读取用了5层抽象
public interface ConfigSource { }
public interface ConfigAdapter extends ConfigSource { }
public abstract class AbstractConfigFactory { }
public class ConcreteConfigFactory extends AbstractConfigFactory { }
public class ConfigManager {
private AbstractConfigFactory factory;
// ... 5个类就为了读一个配置文件?
}
// ✅ 够用就行
public class AppConfig {
private static Properties props;
static {
props = new Properties();
try { props.load(new FileInputStream("config.properties")); }
catch (IOException e) { throw new RuntimeException(e); }
}
public static String get(String key) { return props.getProperty(key); }
}
// 简单、直接、够用!
附录F:快速记忆口诀
创建型5种(工单建抽原):
- 工(工厂方法):一个产品一个工厂
- 单(单例):全局唯一
- 建(建造者):链式构建复杂对象
- 抽(抽象工厂):整族产品一起创建
- 原(原型):克隆现有对象
结构型7种(适桥组装外享代):
- 适(适配器):接口转换
- 桥(桥接):两维度独立变化
- 组(组合):树形结构
- 装(装饰器):功能叠加
- 外(外观):简化接口
- 享(享元):共享节省内存
- 代(代理):控制访问
行为型11种(责命迭中备观状策模访解):
- 责(责任链):请求沿链传递
- 命(命令):请求封装成对象
- 迭(迭代器):顺序访问集合
- 中(中介者):集中协调
- 备(备忘录):存档读档
- 观(观察者):发布订阅
- 状(状态):状态决定行为
- 策(策略):算法可替换
- 模(模板方法):固定骨架
- 访(访问者):不改类加操作
- 解(解释器):语法解析
全文完。感谢阅读,如果对你有帮助,请分享给更多学习 Java 的朋友!
版本:1.0 | 字数:约5万字 | 适合:Java初学者到中级开发者