设计模式

设计原则

1.SOLID

1.S-单一职责原则

类的设计要高内聚低耦合,一个类只有一个引发其变化的原因。

2.O-开闭原则

系统要扩展功能,而不是修改原有代码

3.L-里氏替换原则

子类必须能够代替父类,并保持其正确性

4.I-接口隔离原则

客户端不应该被迫依赖其不使用的方法

5.D-依赖倒置原则

高层模块不应该依赖低层模块,两者都应该依赖抽象。

2.其他设计原则

1.迪米特法则

一个对象应该对其他对象有尽可能少的了解。

降低耦合,避免过长调用链

2.合成复用原则

优先使用组合,而不是继承。

意义:继承是“强耦合”,组合更灵活。

设计模式

创建型模式

1.单例模式

核心思想:某个类只有一个实例,并且提供一个全局访问点来获取这个实例。
结构:构造器私有化 + 静态实例 + 全局访问方法。
适用场景:配置对象、线程池、日志系统。
优点:控制全局唯一对象,节省资源。
缺点:全局状态可能导致代码难以测试。
应用Runtime.getRuntime(),Spring 默认 bean 单例。

1.饿汉式(Eager Initialization)

1
2
3
4
5
6
7
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}

特点:饿汉式在类加载时就创建对象,即在类的Initialization(加载)阶段创建。

优点:线程安全简单。

缺点:一开始就加载可能会导致内存浪费。

2.懒汉式(Lazy Initialization,线程不安全)

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

特点:只有在需要用到这个实例时才进行第一次创建。

缺点:多线程环境下,可能有多个线程同时进入if(instance==null)中,导致创建多个实例。

3.懒汉式(线程安全版)

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

  1. 在实例方法加锁
1
2
3
4
public synchronized void foo() {
// ...
}

等价于

1
2
3
4
5
6
public void foo() {
synchronized(this) {
// ...
}
}

  • 锁的对象是 当前实例 (this)

  • 同一个对象的多个 synchronized 实例方法,调用时会互斥。

  • 不同对象之间互不影响。

  1. 在静态方法加锁
1
2
3
4
public static synchronized void bar() {
// ...
}

等价于

1
2
3
4
5
6
public static void bar() {
synchronized (Singleton.class) {
// ...
}
}

  • 锁的对象是 类的 Class 对象

  • 整个类范围内(所有实例)共享一把锁。无论从哪个对象或类名调用,都要竞争 同一把类锁

特点:因此懒汉式的线程安全版加的是静态方法锁,即对整个类的对象加锁。

优点:线程安全

缺点:每次调用都需要加锁,效率低。

4.双重检查锁(DCL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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关键字防止指令重排,在Java中创建一个对象可以大概分为三步:

1
2
3
4
memory = allocate();         // 1. 分配内存
ctorInstance(memory); // 2. 初始化对象
instance = memory; // 3. 将 instance 指向内存

而JIT和CPU的指令重排会导致

1
2
3
memory = allocate();         // 1. 分配内存
instance = memory; // 2. 将 instance 指向内存
ctorInstance(memory); // 3. 初始化对象

那么一个对象在if(instance==null)可能会直接return一个存在引用但是未初始化完成的对象。

优点:对比整体加锁,外层的if循环可以在已经存在实例时直接返回单例对象。

5.静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private Singleton() {}

private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}

public static Singleton getInstance() {
return Holder.INSTANCE;
}
}

外部类 Singleton 的加载
当 JVM 加载 Singleton.class 时,并不会立即加载 Holder 类。
所以此时单例对象 还没有被创建

Holder 的加载与初始化
只有在第一次调用 Singleton.getInstance() 时,才会触发对 Holder 类的主动使用,从而导致 Holder加载并初始化
Holder 初始化阶段,INSTANCE = new Singleton() 才会执行。

线程安全保证
JVM 在类的初始化阶段会保证:

  • <clinit>() 方法(即类的静态初始化逻辑)对同一个类只会被执行一次
  • 并且是由 JVM 保证的 同步过程
    因此不需要 synchronized,天然线程安全。

特点:采用了JVM的同步过程保证只会加载一次。

6.枚举类

1
2
3
4
5
6
7
public enum Singleton {
INSTANCE;

public void doSomething() {
System.out.println("working...");
}
}

特点:天然防止反射和序列化破坏。反射破坏不了(JVM 层面禁止),序列化也破坏不了(JVM 自动处理)。

缺点:语义上可能不如类清晰。

SLF4J的单例模式

LoggerFactory中我们可以得到一个存储各个类的ConcurrentHashMap,使用其保证每个类只有一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LoggerFactory {
// 使用ConcurrentHashMap存储已创建的Logger实例
private static final ConcurrentHashMap<String, Logger> loggerMap = new ConcurrentHashMap<>();

public static Logger getLogger(Class<?> clazz) {
return getLogger(clazz.getName());
}

public static Logger getLogger(String name) {
// 先检查缓存中是否存在
Logger logger = loggerMap.get(name);
if (logger != null) {
return logger;
}

// 不存在则创建,并放入缓存
logger = new Logger(name);
Logger existingLogger = loggerMap.putIfAbsent(name, logger);
return existingLogger != null ? existingLogger : logger;
}
}

对于每一个类创建唯一一个Logger对象,使用ConcurrentHashMap保持线程安全

1
2
3
4
5
// 在类A中
private static final Logger logger = LoggerFactory.getLogger(ClassA.class);

// 在类B中
private static final Logger logger = LoggerFactory.getLogger(ClassB.class);

2.工厂方法模式

核心思想:定义一个创建对象的接口,让子类决定实例化哪个类。
结构:抽象工厂接口 + 具体工厂实现。
适用场景:需要根据环境决定创建的对象。
优点:解耦对象创建和使用。
缺点:类数量增加。
应用:SLF4J的LoggerFactory,JDBC的DriverManager.getConnection()Iterator<E> iteator()方法

工厂方法模式主要有以下角色:

  1. Product(产品接口/抽象类)
    定义产品的抽象(如 Button 接口)。
1
2
3
public interface Button {
void render();
}
  1. ConcreteProduct(具体产品类)
    实现 Product 接口(如 WindowsButton, MacButton)。
1
2
3
4
5
6
7
8
9
10
11
12
public class WindowsButton implements Button {
public void render() {
System.out.println("Render a Windows style button");
}
}

public class MacButton implements Button {
public void render() {
System.out.println("Render a Mac style button");
}
}

  1. Creator(抽象工厂/创建者)
    声明工厂方法,返回 Product。
1
2
3
4
5
6
7
8
9
10
11
public abstract class Dialog {
// 工厂方法,返回抽象产品
public abstract Button createButton();

// 使用工厂方法
public void renderWindow() {
Button okButton = createButton();
okButton.render();
}
}

  1. ConcreteCreator(具体工厂类)
    实现工厂方法,返回具体的 ConcreteProduct。
1
2
3
4
5
6
7
8
9
10
11
public class WindowsDialog extends Dialog {
public Button createButton() {
return new WindowsButton();
}
}

public class MacDialog extends Dialog {
public Button createButton() {
return new MacButton();
}
}
  1. 客户端实例化过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
Dialog dialog;

String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
dialog = new WindowsDialog();
} else {
dialog = new MacDialog();
}

dialog.renderWindow(); // 使用工厂方法创建按钮
}
}

优点

  • 符合 开闭原则:新增产品时,只需要新增工厂类,不改原有代码。
  • 客户端只依赖抽象,降低耦合。
  • 容易扩展产品族。

缺点

  • 每增加一个产品类,就要写一个新的工厂类,类的数量会增加。
  • 结构比简单工厂复杂。

3.抽象工厂方法模式

抽象工厂模式是一种创建型设计模式,它提供一个接口,可以创建一系列相关或相互依赖的对象,而无需指定它们的具体类。

AbstractProduct(抽象产品)
每个产品的抽象(例如 Button, Checkbox)。

1
2
3
4
5
6
7
8
9
10
// 按钮接口
public interface Button {
void paint();
}

// 复选框接口
public interface Checkbox {
void paint();
}

ConcreteProduct(具体产品)
具体产品实现(如 WindowsButton, MacButton)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class WindowsButton implements Button {
public void paint() {
System.out.println("Render a button in Windows style.");
}
}

public class MacButton implements Button {
public void paint() {
System.out.println("Render a button in Mac style.");
}
}

public class WindowsCheckbox implements Checkbox {
public void paint() {
System.out.println("Render a checkbox in Windows style.");
}
}

public class MacCheckbox implements Checkbox {
public void paint() {
System.out.println("Render a checkbox in Mac style.");
}
}

AbstractFactory(抽象工厂)
定义创建一组抽象产品的方法。

1
2
3
4
5
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}

ConcreteFactory(具体工厂)
实现抽象工厂的方法,生产具体产品族。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}

public class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}

Client(客户端)
通过抽象工厂获取产品,只依赖抽象,不关心具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Application {
private Button button;
private Checkbox checkbox;

public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}

public void render() {
button.paint();
checkbox.paint();
}

public static void main(String[] args) {
GUIFactory factory;

String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
factory = new WindowsFactory();
} else {
factory = new MacFactory();
}

Application app = new Application(factory);
app.render();
}
}

4.建造者模式

建造者模式是一种将复杂对象的构造过程与表示分离的设计模式,使得同样的构建过程可以创建不同的表示。

内部静态类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

public class House {
private String foundation;
private String walls;
private String roof;
private boolean hasGarage;
private boolean hasGarden;

private House(Builder builder) {
this.foundation = builder.foundation;
this.walls = builder.walls;
this.roof = builder.roof;
this.hasGarage = builder.hasGarage;
this.hasGarden = builder.hasGarden;
}

public static class Builder {
private String foundation;
private String walls;
private String roof;
private boolean hasGarage;
private boolean hasGarden;

public Builder foundation(String foundation) {
this.foundation = foundation;
return this;
}
public Builder walls(String walls) {
this.walls = walls;
return this;
}
public Builder roof(String roof) {
this.roof = roof;
return this;
}
public Builder garage(boolean hasGarage) {
this.hasGarage = hasGarage;
return this;
}
public Builder garden(boolean hasGarden) {
this.hasGarden = hasGarden;
return this;
}
public House build() {
return new House(this);
}
}

@Override
public String toString() {
return "House [foundation=" + foundation + ", walls=" + walls +
", roof=" + roof + ", garage=" + hasGarage + ", garden=" + hasGarden + "]";
}
}

客户端

1
2
3
4
5
6
7
8
9
House house = new House.Builder()
.foundation("Concrete")
.walls("Brick")
.roof("Tile")
.garage(true)
.garden(false)
.build();
System.out.println(house);

✅ 优点

  • 将复杂对象的构建和表示分离,代码更清晰。
  • 支持链式调用,易读性强。
  • 可以复用构造步骤,创建不同对象。
  • 符合 单一职责原则:Builder 负责构造,Product 负责表示。

❌ 缺点

  • 类数量可能增多(传统写法需要多个 Builder、Director)。
  • 不适合简单对象(会显得过度设计)。

Lombok 的 @Builder

Lombok 通过在类或构造器/方法上加 @Builder自动生成内部静态 Builder 类,省去手写 Builder 的麻烦。

1
2
3
4
5
6
7
8
9
10
11
12
import lombok.Builder;
import lombok.ToString;

@Builder
@ToString
public class User {
private String name;
private int age;
private String email;
private String password;
}

编译后,Lombok 会帮你生成一个 UserBuilder 类,支持链式调用:

1
2
3
4
5
6
7
8
9
User user = User.builder()
.name("Tom")
.age(20)
.email("tom@example.com")
.password("123456")
.build();

System.out.println(user);

StringBuilder

使用append()不断追加内容,最后通过toString()得到结果。StringBuilder 不是严格的 建造者模式,但用法上体现了 Builder 模式的思想:链式构建、最后生成结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();

String result = sb.append("Hello")
.append(", ")
.append("World")
.append("!")
.toString();

System.out.println(result); // Hello, World!
}
}

5.原型模式

1. 浅拷贝(Shallow Copy)

  • 拷贝对象本身,但引用类型属性只拷贝引用,不拷贝对象本身。
  • 共享引用
  • 可能会引发 改一处动全身 的问题

2. 深拷贝(Deep Copy)

  • 不仅拷贝对象本身,还拷贝它引用的所有对象。
  • 新对象完全独立,不会共享引用。

实现方法:

  • 手写 clone(),在里面递归克隆引用对象。
  • 或者通过序列化/反序列化实现。

Java中应用

  • ArrayList.clone()(浅拷贝)。

    HashMap.clone()(浅拷贝)。

  • Java 提供了 Cloneable 接口 + Object.clone() 方法

  • 但是:

    • Cloneable 是一个标记接口,不定义方法。
    • 必须重写 clone() 并调用 super.clone()
    • 否则会抛出 CloneNotSupportedException

很多框架(如 Spring)更推荐 拷贝构造函数 / 工具类(如 Apache Commons Lang 的 SerializationUtils.clone() 来做深拷贝。

结构型模式

解决 对象和类如何组合 的问题。

6.适配器模式

核心思想:将不兼容的接口转换为可用接口。
结构:Adapter 包装 Adaptee。
适用场景:老代码接口不兼容。
优点:复用已有类。
缺点:类层次复杂化。
应用InputStreamReader,Spring MVC 参数适配器。

  1. 类适配器(继承)
  • Adapter 继承 Adaptee,实现 Target 接口。

  • 通过继承获得 Adaptee 的功能。

  1. 组合适配(持有引用)
  • Adapter 持有 Adaptee 的引用,转发调用。
  • 更灵活(可以适配多个不同的 Adaptee)。
  1. 接口适配器
  • 提供一个抽象类,实现接口的所有方法为空实现。
  • 子类可以选择性地重写需要的方法。
  • 常用于回调接口场景(比如 Java AWT/Swing 的监听器)。

Java中的标准适配器

java.util.Collections#list()不完全是适配器模式,要实现适配器模式类似于以下方式,将Enumeration转为Iterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class EnumerationIterator<T> implements Iterator<T> {
private Enumeration<T> enumeration;

public EnumerationIterator(Enumeration<T> enumeration) {
this.enumeration = enumeration;
}

@Override
public boolean hasNext() {
return enumeration.hasMoreElements();
}

@Override
public T next() {
return enumeration.nextElement();
}
}

java的实现例子

让方钉适配圆孔

圆孔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package refactoring_guru.adapter.example.round;

/**
* RoundHoles are compatible with RoundPegs.
*/
public class RoundHole {
private double radius;

public RoundHole(double radius) {
this.radius = radius;
}

public double getRadius() {
return radius;
}

public boolean fits(RoundPeg peg) {
boolean result;
result = (this.getRadius() >= peg.getRadius());
return result;
}
}

圆钉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package refactoring_guru.adapter.example.round;

/**
* RoundPegs are compatible with RoundHoles.
*/
public class RoundPeg {
private double radius;

public RoundPeg() {}

public RoundPeg(double radius) {
this.radius = radius;
}

public double getRadius() {
return radius;
}
}

方钉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package refactoring_guru.adapter.example.square;

/**
* SquarePegs are not compatible with RoundHoles (they were implemented by
* previous development team). But we have to integrate them into our program.
*/
public class SquarePeg {
private double width;

public SquarePeg(double width) {
this.width = width;
}

public double getWidth() {
return width;
}

public double getSquare() {
double result;
result = Math.pow(this.width, 2);
return result;
}
}

适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package refactoring_guru.adapter.example.adapters;

import refactoring_guru.adapter.example.round.RoundPeg;
import refactoring_guru.adapter.example.square.SquarePeg;

/**
* Adapter allows fitting square pegs into round holes.
*/
public class SquarePegAdapter extends RoundPeg {
private SquarePeg peg;

public SquarePegAdapter(SquarePeg peg) {
this.peg = peg;
}

@Override
public double getRadius() {
double result;
// Calculate a minimum circle radius, which can fit this peg.
result = (Math.sqrt(Math.pow((peg.getWidth() / 2), 2) * 2));
return result;
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package refactoring_guru.adapter.example;

import refactoring_guru.adapter.example.adapters.SquarePegAdapter;
import refactoring_guru.adapter.example.round.RoundHole;
import refactoring_guru.adapter.example.round.RoundPeg;
import refactoring_guru.adapter.example.square.SquarePeg;

/**
* Somewhere in client code...
*/
public class Demo {
public static void main(String[] args) {
// Round fits round, no surprise.
RoundHole hole = new RoundHole(5);
RoundPeg rpeg = new RoundPeg(5);
if (hole.fits(rpeg)) {
System.out.println("Round peg r5 fits round hole r5.");
}

SquarePeg smallSqPeg = new SquarePeg(2);
SquarePeg largeSqPeg = new SquarePeg(20);
// hole.fits(smallSqPeg); // Won't compile.

// Adapter solves the problem.
SquarePegAdapter smallSqPegAdapter = new SquarePegAdapter(smallSqPeg);
SquarePegAdapter largeSqPegAdapter = new SquarePegAdapter(largeSqPeg);
if (hole.fits(smallSqPegAdapter)) {
System.out.println("Square peg w2 fits round hole r5.");
}
if (!hole.fits(largeSqPegAdapter)) {
System.out.println("Square peg w20 does not fit into round hole r5.");
}
}
}

✅ 优点

  • 解耦:客户端和被适配类解耦。
  • 复用:复用已有功能,无需改源码。
  • 灵活:对象适配器方式支持组合多个适配对象。

❌ 缺点

  • 增加了系统复杂度,多了一层转换。
  • 类适配器方式受限于单继承。

7.装饰器模式

核心思想:在不修改对象代码的前提下动态添加功能。
结构:Decorator 包含被装饰对象。
适用场景:多层功能增强。
优点:比继承更灵活。
缺点:多层装饰会难以调试。
应用:Java I/O 流。

在不改变原有对象结构的前提下,动态地给对象增加新的功能。

  • 它通过“包装”一个对象来增强功能,而不是通过继承。
  • 使用同一个接口,保证客户端无感知。

java实现例子

1
2
3
4
5
6
7
8
9
10
interface Component {
void operation();
}

class ConcreteComponent implements Component {
public void operation() {
System.out.println("执行基本操作");
}
}

抽象装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Decorator implements Component {
protected Component component;

public Decorator(Component component) {
this.component = component;
}

@Override
public void operation() {
component.operation(); // 默认转发
}
}

具体装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class LoggingDecorator extends Decorator {
public LoggingDecorator(Component component) {
super(component);
}

@Override
public void operation() {
System.out.println("记录日志...");
super.operation();
}
}

class SecurityDecorator extends Decorator {
public SecurityDecorator(Component component) {
super(component);
}

@Override
public void operation() {
System.out.println("检查权限...");
super.operation();
}
}

客户端使用

1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
Component c = new ConcreteComponent();

// 增强功能:先权限检查,再日志记录,再执行基本操作
Component decorated = new LoggingDecorator(new SecurityDecorator(c));
decorated.operation();
}
}

现实例子

Java IO

  • InputStream 是抽象构件。

  • FileInputStream 是具体构件。

  • BufferedInputStreamDataInputStreamZipInputStream 是装饰器。

  • 它们可以层层嵌套:

    1
    2
    3
    InputStream in = new BufferedInputStream(
    new DataInputStream(
    new FileInputStream("test.txt")));

Spring

  • Spring AOP 的 BeanPostProcessor 本质上也是装饰思想:在 Bean 初始化前后加功能。

GUI 框架

  • 滚动条装饰器 ScrollBarDecorator 可以给窗口动态加滚动条。

✅ 优点

  • 扩展性强:不修改原有类就能添加功能。
  • 灵活组合:不同装饰器可以任意组合。
  • 遵循开闭原则:对扩展开放,对修改关闭。

❌ 缺点

  • 对象层层包装,调试时可能比较复杂。
  • 装饰链过长时,可能影响性能和可读性。

8.代理模式

核心思想:通过代理对象控制对真实对象的访问。
结构:Proxy 持有 RealSubject。
适用场景:远程代理、安全代理、延迟加载。
优点:控制访问、增强功能。
缺点:增加层级,可能影响性能。
应用:Spring AOP、JDK 动态代理。

定义接口

1
2
3
4
interface Subject {
void request();
}

真实主题

1
2
3
4
5
6
7
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("执行真实请求");
}
}

代理主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Proxy implements Subject {
private RealSubject realSubject;

@Override
public void request() {
// 可以做权限控制
System.out.println("检查权限...");

// 延迟初始化(懒加载)
if (realSubject == null) {
realSubject = new RealSubject();
}

// 调用真实对象
realSubject.request();

// 可以做日志
System.out.println("记录日志...");
}
}

客户端

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}

代理模式类型

  1. 静态代理

编译期就写死了代理类。

缺点:如果接口很多,需要写很多代理类。

  1. 动态代理
  • JDK 动态代理:基于 InvocationHandler + 反射,要求被代理对象必须实现接口。
  • CGLIB 动态代理:基于字节码生成,可以代理没有实现接口的类。

Spring AOP 就是基于 动态代理 实现的。

优点

  • 控制访问:隔离客户端与真实对象。
  • 可扩展:代理可在不修改真实对象的前提下增强功能。
  • 提高灵活性:支持远程调用、懒加载等模式。

缺点

  • 可能增加系统复杂度。
  • 动态代理可能影响性能(尤其是反射和字节码生成)。

9.外观模式

子系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 电视
class TV {
public void on() {
System.out.println("TV is on");
}
public void off() {
System.out.println("TV is off");
}
}

// 音响
class SoundSystem {
public void on() {
System.out.println("Sound system is on");
}
public void off() {
System.out.println("Sound system is off");
}
public void setVolume(int volume) {
System.out.println("Setting volume to " + volume);
}
}

// DVD 播放器
class DVDPlayer {
public void on() {
System.out.println("DVD player is on");
}
public void off() {
System.out.println("DVD player is off");
}
public void playMovie(String movie) {
System.out.println("Playing movie: " + movie);
}
}

外观

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 外观类
public class HomeTheaterFacade {
private TV tv;
private SoundSystem soundSystem;
private DVDPlayer dvdPlayer;

public HomeTheaterFacade() {
this.tv = new TV();
this.soundSystem = new SoundSystem();
this.dvdPlayer = new DVDPlayer();
}

// 电影模式:一个简单的方法,背后执行一系列复杂操作
public void watchMovie(String movie) {
System.out.println("准备进入电影模式...");
tv.on();
soundSystem.on();
soundSystem.setVolume(15);
dvdPlayer.on();
dvdPlayer.playMovie(movie);
System.out.println("电影模式准备就绪!");
}

// 结束模式:另一个简单的方法
public void endMovie() {
System.out.println("准备退出电影模式...");
dvdPlayer.off();
soundSystem.off();
tv.off();
System.out.println("所有设备已关闭。");
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 客户端
public class Client {
public static void main(String[] args) {
// 创建外观对象,客户端无需关心子系统的复杂性
HomeTheaterFacade homeTheater = new HomeTheaterFacade();

// 客户端只需调用一个简单的方法
homeTheater.watchMovie("The Matrix");

System.out.println("\n----------------\n");

homeTheater.endMovie();
}
}

Spring 框架

  • JdbcTemplateRedisTemplateRestTemplate 就是外观模式的应用:
    它们对复杂的数据库操作、Redis 操作、HTTP 调用进行了封装,客户端只需调用简单方法。

日志框架(SLF4J)

  • LoggerFactory.getLogger() 就是外观,底层可能是 Logback、Log4j,但客户端只依赖统一接口。

电脑开机

  • 按下电源键(Facade) → 调用 CPU、内存、硬盘等子系统。

API Gateway(微服务网关)

  • 外部系统只调用网关,网关再去调度多个微服务。

✅ 优点

  • 简化调用:屏蔽复杂性,让客户端更容易使用系统。
  • 降低耦合:客户端与子系统解耦,只依赖 Facade。
  • 更安全:外观类可以控制客户端访问的子系统范围。

❌ 缺点

  • 可能成为“上帝类”,职责过多。
  • 增加了一层间接调用,灵活性降低(客户端无法访问到子系统的所有功能)。

10.桥接模式

核心思想:将抽象与实现分离,使它们可以独立变化,解决组合爆炸问题。
结构:Abstraction 持有 Implementor。
适用场景:多维度扩展(颜色+形状)。
优点:避免类爆炸。
缺点:增加抽象层。
应用:JDBC 接口 + 驱动实现。

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 实现者接口:定义消息发送方式
interface MessageSender {
void sendMessage(String message);
}

// 具体实现者:邮件发送
class EmailMessageSender implements MessageSender {
@Override
public void sendMessage(String message) {
System.out.println("通过邮件发送消息: " + message);
}
}

// 具体实现者:短信发送
class SMSMessageSender implements MessageSender {
@Override
public void sendMessage(String message) {
System.out.println("通过短信发送消息: " + message);
}
}

抽象层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 抽象类:定义消息类型
abstract class Message {
protected MessageSender sender;

public Message(MessageSender sender) {
this.sender = sender;
}

// 抽象方法,留给子类实现
public abstract void send(String text);
}

// 细化抽象:普通消息
class NormalMessage extends Message {
public NormalMessage(MessageSender sender) {
super(sender);
}

@Override
public void send(String text) {
// 普通消息直接发送
sender.sendMessage(text);
}
}

// 细化抽象:加急消息
class UrgentMessage extends Message {
public UrgentMessage(MessageSender sender) {
super(sender);
}

@Override
public void send(String text) {
// 加急消息可以添加额外的处理逻辑
text = "加急:" + text;
sender.sendMessage(text);
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
public class Client {
public static void main(String[] args) {
// 桥接:普通消息 + 邮件发送
Message normalMessage = new NormalMessage(new EmailMessageSender());
normalMessage.send("这是一条普通消息。");

// 桥接:加急消息 + 短信发送
Message urgentMessage = new UrgentMessage(new SMSMessageSender());
urgentMessage.send("这是一条加急消息。");
}
}

✅ 优点

  • 避免类爆炸,降低继承层次。
  • 抽象与实现可以独立扩展,互不影响。
  • 符合开闭原则。

❌ 缺点

  • 增加了系统复杂性(要多设计一层 Implementor)。
  • 适合变化维度明显的场景,不适合单一维度系统。

11.组合模式

核心思想:将对象组合成树形结构,使用户对单个对象和组合对象使用一致接口。
结构:Component → Leaf / Composite。
适用场景:层次结构(文件夹/文件)。
优点:一致性操作。
缺点:难以限制组合层次。
应用:Swing 组件树、XML DOM。

抽象组件

1
2
3
interface FileSystemNode {
void show();
}

叶子节点

1
2
3
4
5
6
7
8
9
10
11
12
13
class FileNode implements FileSystemNode {
private String name;

public FileNode(String name) {
this.name = name;
}

@Override
public void show() {
System.out.println("文件: " + name);
}
}

容器节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.ArrayList;
import java.util.List;

class DirectoryNode implements FileSystemNode {
private String name;
private List<FileSystemNode> children = new ArrayList<>();

public DirectoryNode(String name) {
this.name = name;
}

public void add(FileSystemNode node) {
children.add(node);
}

@Override
public void show() {
System.out.println("目录: " + name);
for (FileSystemNode child : children) {
child.show();
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {
public static void main(String[] args) {
FileSystemNode file1 = new FileNode("a.txt");
FileSystemNode file2 = new FileNode("b.txt");

DirectoryNode folder = new DirectoryNode("docs");
folder.add(file1);
folder.add(file2);

DirectoryNode root = new DirectoryNode("root");
root.add(folder);
root.add(new FileNode("c.txt"));

root.show();
}
}

文件系统

  • 文件和文件夹统一看作“节点”。

组织架构

  • 员工(Leaf)和部门(Composite)都实现 OrganizationUnit 接口。

菜单系统(GUI)

  • 菜单项(Leaf)和子菜单(Composite)统一处理。

图形绘制系统

  • 线段、圆形(Leaf),组合图形(Composite)。

✅ 优点

  • 统一性:客户端可以统一处理单个对象和组合对象。
  • 扩展性强:新增叶子节点或组合节点时,不影响已有代码。
  • 层次结构清晰:天然适合树形数据表示。

❌ 缺点

  • 系统会产生过多的细粒度对象(节点多时内存占用高)。
  • 控制粒度可能变弱(比如有些方法只适用于叶子,但接口统一后容易被误用)。

12.享元模式

核心思想:共享对象,减少内存消耗。
结构:FlyweightFactory 管理共享对象。
适用场景:大量细粒度对象(字符、棋子)。
优点:节省内存。
缺点:逻辑复杂化。
应用Integer.valueOf() 缓存,JVM 字符串常量池。

抽象享元

1
2
3
4
interface Flyweight {
void operation(String extrinsicState);
}

具体享元

1
2
3
4
5
6
7
8
9
10
11
12
13
class ConcreteFlyweight implements Flyweight {
private final String intrinsicState; // 内部状态(共享)

public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}

@Override
public void operation(String extrinsicState) {
System.out.println("内部状态: " + intrinsicState + ",外部状态: " + extrinsicState);
}
}

享元工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.HashMap;
import java.util.Map;

class FlyweightFactory {
private final Map<String, Flyweight> pool = new HashMap<>();

public Flyweight getFlyweight(String key) {
if (!pool.containsKey(key)) {
pool.put(key, new ConcreteFlyweight(key));
}
return pool.get(key);
}

public int poolSize() {
return pool.size();
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();

Flyweight f1 = factory.getFlyweight("A");
Flyweight f2 = factory.getFlyweight("A");
Flyweight f3 = factory.getFlyweight("B");

f1.operation("位置1");
f2.operation("位置2");
f3.operation("位置3");

System.out.println("享元池大小: " + factory.poolSize());
}
}

Java String 常量池

  • "abc" 字符串字面量会被放入常量池,复用相同字符串对象。

Java Integer 缓存

  • Integer.valueOf(127) 总是返回缓存对象,避免频繁创建。

数据库连接池、线程池

  • 资源复用,避免频繁创建销毁。

字体渲染、图标缓存

  • 文本编辑器中重复出现的字符、图标只创建一个对象,位置作为外部状态。

游戏对象

  • 子弹、树木、草地等大量相同对象可以复用。

✅ 优点

  • 大大减少内存中对象的数量,提升性能。
  • 共享对象更容易集中管理,节省系统资源。

❌ 缺点

  • 引入了区分内部状态和外部状态的复杂性。
  • 外部状态需要由客户端维护,增加了使用成本。
  • 过度使用可能导致系统结构不清晰。

行为型模式

观察者模式

核心思想:对象状态改变时通知所有依赖者。
结构:Subject + Observer。
适用场景:事件订阅。
优点:解耦发布与订阅。
缺点:链式通知可能影响性能。
应用:Java Observer,GUI 事件监听。

基础发布者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package refactoring_guru.observer.example.publisher;

import refactoring_guru.observer.example.listeners.EventListener;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EventManager {
Map<String, List<EventListener>> listeners = new HashMap<>();

public EventManager(String... operations) {
for (String operation : operations) {
this.listeners.put(operation, new ArrayList<>());
}
}

public void subscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.add(listener);
}

public void unsubscribe(String eventType, EventListener listener) {
List<EventListener> users = listeners.get(eventType);
users.remove(listener);
}

public void notify(String eventType, File file) {
List<EventListener> users = listeners.get(eventType);
for (EventListener listener : users) {
listener.update(eventType, file);
}
}
}

具体发布者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package refactoring_guru.observer.example.editor;

import refactoring_guru.observer.example.publisher.EventManager;

import java.io.File;

public class Editor {
public EventManager events;
private File file;

public Editor() {
this.events = new EventManager("open", "save");
}

public void openFile(String filePath) {
this.file = new File(filePath);
events.notify("open", file);
}

public void saveFile() throws Exception {
if (this.file != null) {
events.notify("save", file);
} else {
throw new Exception("Please open a file first.");
}
}
}

✅ 优点

  • 解耦:主题和观察者之间是抽象依赖,不直接耦合。
  • 灵活扩展:增加新的观察者无需修改主题代码。
  • 支持广播通信:一个事件可以通知多个对象。

❌ 缺点

  • 性能问题:观察者数量多时,通知会很耗时。
  • 通知链复杂:若观察者之间有依赖,可能引发级联更新,难以调试。
  • 内存泄漏风险:如果观察者忘记移除引用,可能导致对象无法被回收。

策略模式

核心思想:封装一系列算法,使它们可以互换。
结构:Context + Strategy。
适用场景:不同算法可替换。
优点:避免大量 if-else
缺点:客户端必须知道所有策略。
应用:支付方式选择。

定义一系列算法(策略),将每个算法封装起来,并使它们可以相互替换,让算法的变化独立于使用它的客户端。

📌 简单说:

  • 把 if-else/switch 拆出来,封装成可替换的策略类。
  • 客户端只依赖抽象策略,具体实现由外部注入。

环境类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PayContext {
private PayStrategy strategy;

public PayContext(PayStrategy strategy) {
this.strategy = strategy;
}

public void setStrategy(PayStrategy strategy) {
this.strategy = strategy;
}

public void execute(double amount) {
strategy.pay(amount);
}
}

策略接口

1
2
3
4
interface PayStrategy {
void pay(double amount);
}

具体策略类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class AliPay implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付:" + amount + " 元");
}
}

class WeChatPay implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("使用微信支付:" + amount + " 元");
}
}

class CreditCardPay implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("使用信用卡支付:" + amount + " 元");
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Client {
public static void main(String[] args) {
PayContext context = new PayContext(new AliPay());
context.execute(100);

context.setStrategy(new WeChatPay());
context.execute(200);

context.setStrategy(new CreditCardPay());
context.execute(300);
}
}

Java 8 开始支持 lambda 方法, 它可作为一种替代策略模式的简单方式。

这里有一些核心 Java 程序库中策略模式的示例:

✅ 优点

  • 遵循 开闭原则:新增策略不需要改 Context。
  • 避免大量 if-else。
  • 算法可自由切换,灵活性高。
  • 策略类可以复用。

❌ 缺点

  • 会增加类的数量,每个策略都是一个类。
  • 客户端必须了解不同策略的区别,才能正确选择。
  • 如果策略过多,维护成本可能变高。

责任链模式

核心思想:多个对象依次处理请求,直到被处理。
结构:Handler 链。
适用场景:请求过滤、拦截器。
优点:解耦请求与处理者。
缺点:调试困难,可能无人处理请求。
应用:Servlet 过滤器链,日志框架。

  1. Handler(抽象处理者)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class Approver {
protected Approver next; // 下一个处理者
protected String name;

public Approver(String name) {
this.name = name;
}

public void setNext(Approver next) {
this.next = next;
}

public abstract void approve(int days);
}

  1. ConcreteHandler(具体处理者)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    class TeamLeader extends Approver {
    public TeamLeader(String name) { super(name); }

    @Override
    public void approve(int days) {
    if (days <= 1) {
    System.out.println(name + " 批准了 " + days + " 天假");
    } else if (next != null) {
    next.approve(days);
    }
    }
    }

    class Manager extends Approver {
    public Manager(String name) { super(name); }

    @Override
    public void approve(int days) {
    if (days <= 3) {
    System.out.println(name + " 批准了 " + days + " 天假");
    } else if (next != null) {
    next.approve(days);
    }
    }
    }

    class Director extends Approver {
    public Director(String name) { super(name); }

    @Override
    public void approve(int days) {
    if (days <= 7) {
    System.out.println(name + " 批准了 " + days + " 天假");
    } else if (next != null) {
    next.approve(days);
    }
    }
    }

    class CEO extends Approver {
    public CEO(String name) { super(name); }

    @Override
    public void approve(int days) {
    System.out.println(name + " 批准了 " + days + " 天假");
    }
    }

  2. Client(客户端)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Client {
public static void main(String[] args) {
// 构建责任链
Approver leader = new TeamLeader("组长");
Approver manager = new Manager("经理");
Approver director = new Director("总监");
Approver ceo = new CEO("CEO");

leader.setNext(manager);
manager.setNext(director);
director.setNext(ceo);

// 发起请求
leader.approve(1);
leader.approve(3);
leader.approve(5);
leader.approve(10);
}
}

Java Servlet Filter(过滤器链)

  • 请求经过一系列 Filter(编码过滤器、权限过滤器、日志过滤器)。
  • 每个 Filter 要么处理,要么传递。
  • javax.servlet.Filter#doFilter()

Spring AOP 拦截器链

  • 多个切面(事务、日志、安全)依次执行。

日志处理

审批流程、工作流引擎

  • 常见于 OA 系统、请假审批、费用报销。

✅ 优点

  • 遵循 开闭原则:新增处理者只需要加类,不影响现有逻辑。
  • 请求和处理逻辑解耦,灵活性高。
  • 可以动态组合处理链。

❌ 缺点

  • 如果链太长,请求可能会经过很多节点才被处理,效率低。
  • 不保证一定会被处理(可能没人接收)。
  • 调试和追踪较麻烦。

状态模式

核心思想:对象行为随内部状态改变而改变。
结构:Context + State。
适用场景:状态机。
优点:消除 if-else
缺点:类数量增加。
应用:TCP 连接状态。

Context(环境类)

  • 持有状态对象。
  • 对外提供接口,但内部调用委托给当前状态对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class OrderContext {
private OrderState state;

public OrderContext() {
this.state = new PendingPayment(); // 初始状态
}

public void setState(OrderState state) {
this.state = state;
}

public void request() {
state.handle(this);
}
}

State(抽象状态)

  • 定义不同状态下的公共行为接口。
1
2
3
4
interface OrderState {
void handle(OrderContext context);
}

ConcreteState(具体状态)

  • 实现具体状态下的逻辑。
  • 可以决定是否切换到其他状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class PendingPayment implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("订单待支付,执行支付操作...");
context.setState(new Paid()); // 切换到已支付状态
}
}

class Paid implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("订单已支付,执行发货操作...");
context.setState(new Shipped()); // 切换到已发货状态
}
}

class Shipped implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("订单已发货,执行确认收货操作...");
context.setState(new Completed()); // 切换到已完成状态
}
}

class Completed implements OrderState {
@Override
public void handle(OrderContext context) {
System.out.println("订单已完成,不可操作。");
}
}

✅ 优点

  • 遵循 开闭原则:新增状态不影响已有逻辑。
  • 状态切换逻辑清晰,避免大量 if-else。
  • 把与状态相关的行为封装在独立类里,职责清晰。

❌ 缺点

  • 状态类数量可能过多,增加系统复杂度。
  • 状态切换的逻辑分散在各个状态类里,维护时可能比较麻烦。

命令模式

核心思想:将请求封装为对象,支持撤销/排队操作。
结构:Invoker + Command + Receiver。
适用场景:事务、日志操作。
优点:解耦请求与执行。
缺点:类数量增加。
应用:GUI 按钮命令,数据库事务日志。

抽象命令接口

1
2
3
4
5
interface Command {
void execute();
void undo(); // 支持撤销
}

具体命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class LightOnCommand implements Command {
private Light light;

public LightOnCommand(Light light) {
this.light = light;
}

@Override
public void execute() {
light.turnOn();
}

@Override
public void undo() {
light.turnOff();
}
}

class LightOffCommand implements Command {
private Light light;

public LightOffCommand(Light light) {
this.light = light;
}

@Override
public void execute() {
light.turnOff();
}

@Override
public void undo() {
light.turnOn();
}
}

接收者

1
2
3
4
5
6
7
8
9
10
class Light {
public void turnOn() {
System.out.println("电灯已打开");
}

public void turnOff() {
System.out.println("电灯已关闭");
}
}

调用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class RemoteControl {
private Command command;

public void setCommand(Command command) {
this.command = command;
}

public void pressButton() {
command.execute();
}

public void pressUndo() {
command.undo();
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Client {
public static void main(String[] args) {
Light light = new Light();

Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);

RemoteControl remote = new RemoteControl();

remote.setCommand(lightOn);
remote.pressButton(); // 打开灯
remote.pressUndo(); // 撤销 → 关灯

remote.setCommand(lightOff);
remote.pressButton(); // 关灯
remote.pressUndo(); // 撤销 → 开灯
}
}

✅ 优点

  • 解耦:调用者与接收者解耦,扩展性高。
  • 开闭原则:新增命令只需增加类,不影响已有代码。
  • 可撤销/重做:命令对象可记录历史,支持回滚。
  • 组合命令:可实现宏命令(多个命令一起执行)。

❌ 缺点

  • 会增加类的数量(每个命令一个类)。
  • 如果命令过多,代码结构可能比较复杂。

迭代器模式

核心思想:顺序访问集合元素而不暴露内部结构。
结构:Iterator 接口 + Aggregate。
适用场景:集合遍历。
优点:统一遍历方式。
缺点:额外开销。
应用:Java 的 Iterator 接口。

提供一种方法,顺序访问聚合对象中的各个元素,而不暴露其内部表示。

📌 通俗说:

  • 不管集合是数组、链表还是树,用户只需要用迭代器按顺序取元素,不必关心内部数据结构
  • 集合类负责存储数据,迭代器负责遍历数据。

Java接口

1
2
3
4
5
6
List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

✅ 优点

  • 遵循 单一职责原则:集合负责存储,迭代器负责遍历。
  • 封装性好:不用暴露集合的内部结构。
  • 可扩展性强:支持多种遍历方式(正向、反向、跳跃)。

❌ 缺点

  • 迭代器对象会额外消耗内存。
  • 如果集合被修改,迭代器可能失效(Java 的 fail-fast 机制)。

备忘录模式

核心思想:保存对象内部状态,支持撤销。
结构:Originator + Memento + Caretaker。
适用场景:编辑器撤销、游戏存档。
优点:封装状态。
缺点:内存消耗大。
应用:IDE Undo 功能。

发起人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class TextEditor {
private String content;

public void setContent(String content) {
this.content = content;
}

public String getContent() {
return content;
}

// 创建备忘录
public Memento save() {
return new Memento(content);
}

// 从备忘录恢复
public void restore(Memento memento) {
this.content = memento.getContent();
}

// 内部类,保证外界不能随便操作
public static class Memento {
private final String content;

private Memento(String content) {
this.content = content;
}

private String getContent() {
return content;
}
}
}

管理人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Caretaker {
private Stack<TextEditor.Memento> history = new Stack<>();

public void save(TextEditor editor) {
history.push(editor.save());
}

public void undo(TextEditor editor) {
if (!history.isEmpty()) {
editor.restore(history.pop());
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Client {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
Caretaker caretaker = new Caretaker();

editor.setContent("第一次输入");
caretaker.save(editor);

editor.setContent("第二次输入");
caretaker.save(editor);

editor.setContent("第三次输入");
System.out.println("当前内容: " + editor.getContent());

caretaker.undo(editor);
System.out.println("撤销一次: " + editor.getContent());

caretaker.undo(editor);
System.out.println("撤销两次: " + editor.getContent());
}
}

✅ 优点

  • 封装性好:不暴露对象的内部细节。
  • 简化了发起人:只需要保存/恢复,不关心历史的管理。
  • 符合 单一职责原则:状态的存储交给备忘录,历史管理交给看护者。

❌ 缺点

  • 可能消耗大量内存(如果频繁保存状态)。
  • 在复杂对象里,备份状态的开销可能很大。
  • 如果需要支持“增量存档”,实现会变复杂。

解释器模式

核心思想:为语言定义文法,并解释执行。
结构:Expression 接口 + Terminal/NonTerminal。
适用场景:简单语言解析。
优点:扩展性强。
缺点:复杂文法难以维护。
应用:正则表达式解析器。

抽象表达式

1
2
3
4
interface Expression {
boolean interpret(Map<String, Boolean> context);
}

终结符表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
class VariableExpression implements Expression {
private String name;

public VariableExpression(String name) {
this.name = name;
}

@Override
public boolean interpret(Map<String, Boolean> context) {
return context.get(name);
}
}

非终结符表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class AndExpression implements Expression {
private Expression left, right;

public AndExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}

@Override
public boolean interpret(Map<String, Boolean> context) {
return left.interpret(context) && right.interpret(context);
}
}

class OrExpression implements Expression {
private Expression left, right;

public OrExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}

@Override
public boolean interpret(Map<String, Boolean> context) {
return left.interpret(context) || right.interpret(context);
}
}

class NotExpression implements Expression {
private Expression exp;

public NotExpression(Expression exp) {
this.exp = exp;
}

@Override
public boolean interpret(Map<String, Boolean> context) {
return !exp.interpret(context);
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Client {
public static void main(String[] args) {
// 构建表达式: (a AND b) OR (c AND NOT d)
Expression a = new VariableExpression("a");
Expression b = new VariableExpression("b");
Expression c = new VariableExpression("c");
Expression d = new VariableExpression("d");

Expression expr = new OrExpression(
new AndExpression(a, b),
new AndExpression(c, new NotExpression(d))
);

// 上下文赋值
Map<String, Boolean> context = new HashMap<>();
context.put("a", true);
context.put("b", false);
context.put("c", true);
context.put("d", false);

// 解释
boolean result = expr.interpret(context);
System.out.println("结果: " + result);
}
}

✅ 优点

  • 易于扩展:每个文法规则对应一个类,新增规则时只需新增类。
  • 代码结构清晰:将复杂表达式的解析逻辑拆分成对象树。
  • 符合 开闭原则

❌ 缺点

  • 类数量可能爆炸(每个规则一个类)。
  • 对于复杂文法性能差,不如 编译原理里的解析器生成工具(ANTLR、Yacc)
  • 可读性下降,大量小类不利于维护。

中介者模式

核心思想:通过中介对象封装对象之间的交互。
结构:Mediator + Colleague。
适用场景:复杂交互。
优点:减少对象间耦合。
缺点:中介者可能过于复杂。
应用:MVC 的 Controller。

抽象中介者

1
2
3
4
5
interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}

具体中介者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();

@Override
public void sendMessage(String msg, User user) {
for (User u : users) {
if (u != user) {
u.receive(msg);
}
}
}

@Override
public void addUser(User user) {
users.add(user);
}
}

抽象同事类

1
2
3
4
5
6
7
8
9
10
11
12
13
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 msg);
public abstract void receive(String msg);
}

具体同事类

1
2
3
4
5
6
7
8
9
10
11
12
13
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 msg);
public abstract void receive(String msg);
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {
public static void main(String[] args) {
ChatMediator chat = new ChatRoom();

User u1 = new ChatUser(chat, "Alice");
User u2 = new ChatUser(chat, "Bob");
User u3 = new ChatUser(chat, "Charlie");

chat.addUser(u1);
chat.addUser(u2);
chat.addUser(u3);

u1.send("大家好!");
u2.send("你好 Alice!");
}
}

GUI 框架

  • 窗口、按钮、输入框之间的交互,通常用中介者(对话框管理器)。

聊天室 / 消息队列 / 事件总线

  • 用户或模块之间通过中介者(聊天室、消息中心)通信。

航班调度系统

  • 飞机(同事)通过塔台(中介者)协调起降。

Spring ApplicationContext

  • Bean 之间的依赖关系,通过 IoC 容器调度,本质上也是一种中介者思想。

✅ 优点

  • 降低对象之间的耦合(解耦合)。
  • 将交互逻辑集中到一个中介者,便于维护。
  • 符合 迪米特法则(最少知识原则):对象只与中介者交互。

❌ 缺点

  • 中介者本身可能变得过于复杂,演变成“上帝类”。
  • 过度使用会让逻辑集中在一个类里,维护困难。

模板方法模式

核心思想:定义算法骨架,子类实现具体步骤。
结构:抽象类 + 具体实现类。
适用场景:算法流程固定,但部分步骤变化。
优点:复用流程逻辑。
缺点:子类自由度受限。
应用AbstractList,Spring JdbcTemplate

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class DataProcessor {
// 模板方法,规定流程
public final void process() {
readData();
processData();
writeData();
}

protected abstract void readData();
protected abstract void processData();
protected abstract void writeData();
}

具体子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class CSVDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("读取 CSV 数据");
}

@Override
protected void processData() {
System.out.println("处理 CSV 数据");
}

@Override
protected void writeData() {
System.out.println("写入 CSV 结果");
}
}

class ExcelDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("读取 Excel 数据");
}

@Override
protected void processData() {
System.out.println("处理 Excel 数据");
}

@Override
protected void writeData() {
System.out.println("写入 Excel 结果");
}
}

客户端

1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
DataProcessor csv = new CSVDataProcessor();
csv.process();

DataProcessor excel = new ExcelDataProcessor();
excel.process();
}
}

✅ 优点

  • 封装不变部分,扩展可变部分。
  • 遵循 开闭原则:新增子类即可扩展新的算法变种。
  • 代码复用:把公共部分提取到父类。

❌ 缺点

  • 子类数量可能过多(每个变化点一个子类)。
  • 继承关系使得父类对子类有一定的限制(不如组合灵活)。

访问者模式

核心思想:将操作从对象结构中分离出来,避免修改类定义。
结构:Visitor + Element。
适用场景:对象结构稳定,操作频繁变化。
优点:增加新操作简单。
缺点:增加新元素困难。
应用:编译器语法树遍历。

访问者接口

1
2
3
4
5
interface Visitor {
void visitFile(FileElement file);
void visitDirectory(DirectoryElement dir);
}

具体访问者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class SizeVisitor implements Visitor {
private int totalSize = 0;

@Override
public void visitFile(FileElement file) {
totalSize += file.getSize();
}

@Override
public void visitDirectory(DirectoryElement dir) {
for (Element e : dir.getChildren()) {
e.accept(this); // 递归访问
}
}

public int getTotalSize() { return totalSize; }
}

class PrintVisitor implements Visitor {
private int indent = 0;

@Override
public void visitFile(FileElement file) {
System.out.println(" ".repeat(indent) + file.getName() + " (" + file.getSize() + ")");
}

@Override
public void visitDirectory(DirectoryElement dir) {
System.out.println(" ".repeat(indent) + "[" + dir.getName() + "]");
indent++;
for (Element e : dir.getChildren()) {
e.accept(this);
}
indent--;
}
}

元素接口

1
2
3
4
interface Element {
void accept(Visitor visitor);
}

具体元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class FileElement implements Element {
private String name;
private int size;

public FileElement(String name, int size) {
this.name = name;
this.size = size;
}

public int getSize() { return size; }
public String getName() { return name; }

@Override
public void accept(Visitor visitor) {
visitor.visitFile(this);
}
}

class DirectoryElement implements Element {
private String name;
private List<Element> children = new ArrayList<>();

public DirectoryElement(String name) {
this.name = name;
}

public void add(Element e) { children.add(e); }
public List<Element> getChildren() { return children; }
public String getName() { return name; }

@Override
public void accept(Visitor visitor) {
visitor.visitDirectory(this);
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Client {
public static void main(String[] args) {
DirectoryElement root = new DirectoryElement("root");
root.add(new FileElement("a.txt", 10));
root.add(new FileElement("b.txt", 20));

DirectoryElement subDir = new DirectoryElement("sub");
subDir.add(new FileElement("c.txt", 5));
root.add(subDir);

// 打印
PrintVisitor printVisitor = new PrintVisitor();
root.accept(printVisitor);

// 统计大小
SizeVisitor sizeVisitor = new SizeVisitor();
root.accept(sizeVisitor);
System.out.println("总大小: " + sizeVisitor.getTotalSize());
}
}

✅ 优点

  • 符合开闭原则:数据结构不变,增加新操作很容易。
  • 操作集中:将操作逻辑集中到访问者类中,避免分散在元素类里。
  • 支持对不同类型元素做不同操作。

❌ 缺点

  • 增加新元素困难:如果要给结构中加一种新元素,就要修改所有访问者 → 违背开闭原则。
  • 结构复杂:需要双重分派(element.accept(visitor)visitor.visit(element))。
  • 侵入性:元素类必须暴露自己的 accept(),让访问者访问内部。

设计模式
https://yicizhang00.github.io/posts/编程语言/Java/Java基础/设计模式/
作者
Yici Zhang
发布于
2025年8月12日
许可协议