设计模式
欢迎来到第十四阶段——高级专题与分布式。前面阶段我们学了语言机制、工程化、Web 框架。这一阶段我们拔高——设计模式、数据结构算法、Redis、消息队列、微服务、Docker,让你从”会用框架”到”能设计架构”。第一站——设计模式。
设计模式(Design Pattern)是前辈经验的总结——面对特定问题时,“标准答案”长什么样。GoF(Gang of Four)23 种模式是经典。这一章我们看六大设计原则、三大类模式、Java 标准库里的实例。
一、六大设计原则
设计模式的根基是设计原则——遵循原则才能写出好代码,模式只是原则的具体应用。
1.1 SOLID 五原则
| 原则 | 全称 | 含义 |
|---|---|---|
| S - 单一职责 | Single Responsibility | 一个类只有一个变化的原因 |
| O - 开闭原则 | Open-Closed | 对扩展开放,对修改关闭 |
| L - 里氏替换 | Liskov Substitution | 子类能完全替换父类 |
| I - 接口隔离 | Interface Segregation | 接口小而专,不要胖接口 |
| D - 依赖倒置 | Dependency Inversion | 依赖抽象不依赖具体 |
单一职责(SRP) —— 一个类只干一件事。UserService 不该既管用户认证又管发邮件——拆成 UserService 和 EmailService。
开闭原则(OCP) —— 加新功能不改老代码。比如策略模式——加新策略就新增类,不改 Context。
里氏替换(LSP) —— 子类不能违反父类约定。父类 add(a, b) 返回 a+b,子类重写返回 a-b 就违反 LSP——用父类的地方换成子类会出错。
接口隔离(ISP) —— 接口要小。一个 UserService 接口里既有读方法又有写方法,读客户端被迫依赖写方法——拆成 UserReader 和 UserWriter。
依赖倒置(DIP) —— 高层模块不依赖低层模块,都依赖抽象。UserService(高层)不直接依赖 UserRepositoryImpl(低层),依赖 UserRepository 接口——这就是 Spring IoC 的思想。
1.2 迪米特法则(LoD)
迪米特法则(Law of Demeter)——最少知识原则。一个对象只跟”朋友”说话,不跟”陌生人”说话。
// 违反 LoD: 跟陌生人的陌生人说话
order.getCustomer().getAddress().getCity();
// 遵守 LoD: 让直接朋友帮忙
order.getCustomerCity();
不熟的对象别深挖——只调直接依赖对象的方法。降低耦合。
二、创建型模式
创建型模式关注”怎么创建对象”——把创建和使用解耦。
2.1 单例(Singleton)
保证一个类只有一个实例。Java 标准库里 Runtime.getRuntime() 就是单例。
// 枚举单例 (最佳实践, 防反射防序列化)
public enum Singleton {
INSTANCE;
public void doSomething() { ... }
}
// 双重检查锁 (经典写法)
public class Singleton {
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;
}
}
volatile 必加——防止指令重排导致的”半初始化对象泄漏”。
2.2 工厂方法(Factory Method)
定义创建对象的接口,让子类决定实例化哪个类。
interface Product { void use(); }
class ProductA implements Product { public void use() { System.out.println("用 A"); } }
class ProductB implements Product { public void use() { System.out.println("用 B"); } }
abstract class Factory {
abstract Product create();
}
class FactoryA extends Factory { Product create() { return new ProductA(); } }
class FactoryB extends Factory { Product create() { return new ProductB(); } }
// 用
Factory f = new FactoryA();
Product p = f.create(); // ProductA
Spring 的 BeanFactory 就是工厂模式——getBean(name) 返回 Bean 实例。
2.3 建造者(Builder)
把复杂对象的构造和表示分离——一步步构建。
public class User {
private final String name;
private final int age;
private final String email;
private final String address;
private User(Builder b) {
this.name = b.name; this.age = b.age;
this.email = b.email; this.address = b.address;
}
public static class Builder {
private String name; private int age;
private String email; private String address;
public Builder name(String n) { this.name = n; return this; }
public Builder age(int a) { this.age = a; return this; }
public Builder email(String e) { this.email = e; return this; }
public Builder address(String a) { this.address = a; return this; }
public User build() { return new User(this); }
}
}
// 用
User u = new User.Builder()
.name("张三").age(20).email("a@b.com").build();
Lombok 的 @Builder 自动生成这些。StringBuilder/StringBuilder 也是 Builder 模式。
2.4 原型(Prototype)
通过克隆现有对象创建新对象——实现 Cloneable 接口。
public class Prototype implements Cloneable {
private String data;
public Prototype clone() {
try { return (Prototype) super.clone(); }
catch (CloneNotSupportedException e) { throw new RuntimeException(e); }
}
}
Object.clone() 是浅拷贝——基本类型和引用复制,引用指向的对象不复制。深拷贝要递归克隆或序列化。
三、结构型模式
结构型模式关注”类和对象怎么组合”——适配、装饰、代理。
3.1 适配器(Adapter)
让接口不兼容的类能一起工作——把一个接口转换成另一个。
// 旧接口
interface OldPrinter { void printOld(String s); }
class OldPrinterImpl implements OldPrinter {
public void printOld(String s) { System.out.println("旧打印: " + s); }
}
// 新接口
interface NewPrinter { void print(String s); }
// 适配器: 把旧接口包装成新接口
class PrinterAdapter implements NewPrinter {
private OldPrinter old;
public PrinterAdapter(OldPrinter old) { this.old = old; }
public void print(String s) { old.printOld(s); }
}
JDK 的 InputStreamReader 是适配器——把 InputStream(字节流)适配成 Reader(字符流)。
3.2 装饰器(Decorator)
动态给对象加功能——比继承更灵活。
interface Coffee { double cost(); }
class SimpleCoffee implements Coffee {
public double cost() { return 10; }
}
// 装饰器: 加奶
class MilkDecorator implements Coffee {
private Coffee coffee;
public MilkDecorator(Coffee c) { this.coffee = c; }
public double cost() { return coffee.cost() + 2; }
}
// 装饰器: 加糖
class SugarDecorator implements Coffee {
private Coffee coffee;
public SugarDecorator(Coffee c) { this.coffee = c; }
public double cost() { return coffee.cost() + 1; }
}
// 用
Coffee c = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
c.cost(); // 10 + 2 + 1 = 13
JDK 的 BufferedReader/DataInputStream 都是装饰器——给 InputStream 加缓冲、加类型读写。
3.3 代理(Proxy)
给对象加个代理,控制访问。第十阶段的动态代理就是它的应用。
interface Service { void execute(); }
class RealService implements Service {
public void execute() { System.out.println("执行"); }
}
class ProxyService implements Service {
private RealService real;
public void execute() {
if (real == null) real = new RealService();
System.out.println("[前置]");
real.execute();
System.out.println("[后置]");
}
}
3.4 外观(Facade)
给复杂子系统一个统一入口——简化调用。
class CPU { void start() { System.out.println("CPU 启动"); } }
class Memory { void load() { System.out.println("内存加载"); } }
class Disk { void read() { System.out.println("硬盘读取"); } }
class ComputerFacade {
private CPU cpu = new CPU();
private Memory memory = new Memory();
private Disk disk = new Disk();
public void start() {
cpu.start();
memory.load();
disk.read();
System.out.println("电脑启动完成");
}
}
// 用
new ComputerFacade().start(); // 一行搞定, 不用关心内部
Spring 的 JdbcTemplate 就是 Facade——把 JDBC 的复杂操作封装成 query/update 几个方法。
3.5 组合(Composite)
树形结构——统一对待单个对象和组合对象。
interface Component { void display(); }
class Leaf implements Component {
private String name;
public Leaf(String n) { name = n; }
public void display() { System.out.println("叶子: " + name); }
}
class Composite implements Component {
private List<Component> children = new ArrayList<>();
private String name;
public Composite(String n) { name = n; }
public void add(Component c) { children.add(c); }
public void display() {
System.out.println("分支: " + name);
for (Component c : children) c.display();
}
}
Swing 的 Container/Component 是组合模式——容器里能放组件也能放容器。
四、行为型模式
行为型模式关注”对象间怎么通信、职责怎么分配”。
4.1 策略(Strategy)
定义一系列算法,封装起来,可互换——避免一堆 if-else。
interface PayStrategy { void pay(double amount); }
class WechatPay implements PayStrategy {
public void pay(double a) { System.out.println("微信支付 " + a); }
}
class AliPay implements PayStrategy {
public void pay(double a) { System.out.println("支付宝 " + a); }
}
class PayContext {
private PayStrategy strategy;
public PayContext(PayStrategy s) { this.strategy = s; }
public void checkout(double amount) { strategy.pay(amount); }
}
// 用
new PayContext(new WechatPay()).checkout(99.5);
new PayContext(new AliPay()).checkout(99.5);
加新支付方式——新增 PayStrategy 实现类,不改 PayContext。这就是开闭原则。
4.2 观察者(Observer)
一对多依赖——一个对象状态变化,所有依赖者收到通知。
interface Observer { void update(String event); }
class EmailNotifier implements Observer {
public void update(String e) { System.out.println("邮件通知: " + e); }
}
class SmsNotifier implements Observer {
public void update(String e) { System.out.println("短信通知: " + e); }
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer o) { observers.add(o); }
public void notifyAll(String event) {
for (Observer o : observers) o.update(event);
}
}
Spring 的 ApplicationEvent/ApplicationListener 是观察者模式。
4.3 模板方法(Template Method)
定义算法骨架,子类重写特定步骤。
abstract class Game {
public final void play() { // 模板方法, 骨架固定
init();
start();
end();
}
protected abstract void init();
protected abstract void start();
protected void end() { System.out.println("游戏结束"); }
}
class Chess extends Game {
protected void init() { System.out.println("摆棋盘"); }
protected void start() { System.out.println("下棋"); }
}
AbstractList 的 indexOf 是模板方法——它调用子类实现的 size()/get()。
4.4 责任链(Chain of Responsibility)
请求沿链传递,每个处理器决定处理或传给下一个。
abstract class Handler {
protected Handler next;
public Handler setNext(Handler n) { this.next = n; return n; }
public abstract void handle(String request);
}
class AuthHandler extends Handler {
public void handle(String r) {
if (r.contains("auth")) System.out.println("认证通过");
else if (next != null) next.handle(r);
}
}
class LogHandler extends Handler {
public void handle(String r) {
System.out.println("记录日志: " + r);
if (next != null) next.handle(r);
}
}
// 用
Handler chain = new AuthHandler();
chain.setNext(new LogHandler());
chain.handle("auth");
Spring 的 FilterChain、Interceptor 链都是责任链。
4.5 状态(State)
对象状态变化时,行为也变化——把状态封装成类。
interface State { void handle(); }
class IdleState implements State { public void handle() { System.out.println("空闲"); } }
class BusyState implements State { public void handle() { System.out.println("忙"); } }
class Worker {
private State state;
public void setState(State s) { this.state = s; }
public void work() { state.handle(); }
}
五、Java 标准库中的设计模式
| 模式 | JDK 例子 |
|---|---|
| 单例 | Runtime.getRuntime()、System |
| 工厂方法 | Calendar.getInstance()、List.of() |
| 抽象工厂 | DocumentBuilderFactory |
| 建造者 | StringBuilder、Locale.Builder |
| 原型 | Object.clone() |
| 适配器 | InputStreamReader、Arrays.asList() |
| 装饰器 | BufferedReader、DataInputStream |
| 代理 | Proxy、MethodInterceptor |
| 外观 | JdbcTemplate、Files |
| 组合 | Container/Component(Swing) |
| 策略 | Comparator、FileFilter |
| 观察者 | PropertyChangeListener |
| 模板方法 | AbstractList、HttpServlet |
| 责任链 | FilterChain |
| 状态 | Thread.State(部分) |
| 迭代器 | Iterator、Iterable |
设计模式不是”发明”的——它们是对优秀代码的”总结”。JDK 里到处都是。
六、实战:策略 + 工厂重构 if-else
观察重点:
- 策略 + 工厂消除 if-else——加新支付方式只注册不改业务,开闭原则。
- 装饰器自由组合——
Sugar(Milk(Coffee))等于”加糖加奶咖啡”。- 观察者解耦——下单时通知邮件/库存/积分三个观察者,互不影响。
七、本章小结
| 类别 | 模式 |
|---|---|
| 6 原则 | SRP/OCP/LSP/ISP/DIP/LoD |
| 创建型 | 单例/工厂/建造者/原型 |
| 结构型 | 适配器/装饰器/代理/外观/组合 |
| 行为型 | 策略/观察者/模板方法/责任链/状态 |
记忆口诀:
- SOLID 五原则——单、开、里、接、依。
- 策略消除 if-else——每分支一个类,工厂统一找。
- 装饰器比继承灵活——组合优于继承。
- 观察者一对多——事件解耦。
- 模板方法定骨架——子类填细节。
- 责任链串起来——请求一节一节过。
结语:模式是手段不是目的
设计模式是经验的结晶,但别滥用——简单 if-else 三行的逻辑非要上策略模式就是过度设计。模式的本质是”解决特定问题”——遇到问题再看模式,不是拿着模式找问题。下一章看更硬核的——数据结构与算法。