0%

23 种设计模式速记:口诀、Java 示例与常见疑问

这是我自己整理的一份设计模式速记。23 个模式按创建型、结构型、行为型分开,每个模式保留一句核心、一个能跑的小例子,最后把学习过程中卡住的问题汇成 Q&A。

代码是 Java,示例尽量短,能省的行都省了;个别片段补了 import,其余按需自行补。

类型 数量 关心什么
创建型 5 对象怎么来
结构型 7 对象怎么拼
行为型 11 对象怎么交互

口诀:创建型「抽工建单原」,结构型「桥适组装享外代」,其余是行为型。

  • 抽工建单原 = 抽象工厂、工厂方法、建造者、单例、原型
  • 桥适组装享外代 = 桥接、适配器、组合、装饰、享元、外观、代理

创建型模式

Factory 管“造哪个”,Abstract Factory 管“造一族”,Builder 管“怎么一步步造”,Prototype 管“照着造”,Singleton 管“只造一个”。

模式 核心 关键结构
Factory Method 子类决定 new 哪个 抽象工厂类 + 子类实现创建
Abstract Factory 创建一族相关对象 抽象工厂接口 + 多个产品接口
Builder 分步构造复杂对象 Builder + 链式调用 + build
Prototype 克隆现成对象 clone / copy
Singleton 全局唯一实例 私有构造 + 静态实例

1. Factory Method 工厂方法

父类定创建接口,子类决定 new 哪个。

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
// 产品接口
interface Pay {
void pay();
}

// 具体产品
class WechatPay implements Pay {
public void pay() {
System.out.println("微信支付");
}
}

class AliPay implements Pay {
public void pay() {
System.out.println("支付宝支付");
}
}

// 工厂父类:定创建接口
abstract class PayFactory {
abstract Pay createPay();

// 模板方法:调用创建出来的对象
public void doPay() {
Pay pay = createPay();
pay.pay();
}
}

// 子类决定 new 哪个
class WechatPayFactory extends PayFactory {
Pay createPay() {
return new WechatPay();
}
}

class AliPayFactory extends PayFactory {
Pay createPay() {
return new AliPay();
}
}

// 使用
public class Main {
public static void main(String[] args) {
PayFactory factory = new WechatPayFactory();
factory.doPay();
}
}

核心:创建延迟到子类。


2. Abstract Factory 抽象工厂

创建一族相关对象。

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
56
57
58
59
60
61
62
63
64
65
66
67
// 一族产品:按钮 + 文本框
interface Button {
void render();
}

interface TextBox {
void render();
}

// Windows 一族
class WinButton implements Button {
public void render() {
System.out.println("Windows 按钮");
}
}

class WinTextBox implements TextBox {
public void render() {
System.out.println("Windows 文本框");
}
}

// Mac 一族
class MacButton implements Button {
public void render() {
System.out.println("Mac 按钮");
}
}

class MacTextBox implements TextBox {
public void render() {
System.out.println("Mac 文本框");
}
}

// 抽象工厂:创建一族对象
interface UIFactory {
Button createButton();
TextBox createTextBox();
}

class WinFactory implements UIFactory {
public Button createButton() {
return new WinButton();
}
public TextBox createTextBox() {
return new WinTextBox();
}
}

class MacFactory implements UIFactory {
public Button createButton() {
return new MacButton();
}
public TextBox createTextBox() {
return new MacTextBox();
}
}

// 使用
public class Main {
public static void main(String[] args) {
UIFactory factory = new MacFactory();
factory.createButton().render();
factory.createTextBox().render();
}
}

核心:一族产品一起换,不混搭。
工厂方法是一个方法创建一个产品;抽象工厂是一组方法创建一族产品。


3. Builder 建造者

分步构造复杂对象。

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
56
57
58
class Computer {
private String cpu;
private String gpu;
private int ram;

public void setCpu(String cpu) {
this.cpu = cpu;
}

public void setGpu(String gpu) {
this.gpu = gpu;
}

public void setRam(int ram) {
this.ram = ram;
}

public String toString() {
return "CPU=" + cpu + ", GPU=" + gpu + ", RAM=" + ram + "G";
}
}

// 建造者:一步步设值
class ComputerBuilder {
private Computer computer = new Computer();

public ComputerBuilder cpu(String cpu) {
computer.setCpu(cpu);
return this;
}

public ComputerBuilder gpu(String gpu) {
computer.setGpu(gpu);
return this;
}

public ComputerBuilder ram(int ram) {
computer.setRam(ram);
return this;
}

public Computer build() {
return computer;
}
}

// 使用
public class Main {
public static void main(String[] args) {
Computer pc = new ComputerBuilder()
.cpu("i9")
.gpu("RTX 4090")
.ram(64)
.build();

System.out.println(pc);
}
}

核心:构造过程分步,最后 build。
和工厂的区别:工厂关心“创建哪个”,Builder 关心“怎么一步步建”。


4. Prototype 原型

克隆一个现成对象。

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
class Order implements Cloneable {
private String product;
private int count;

public Order(String product, int count) {
this.product = product;
this.count = count;
}

public void setCount(int count) {
this.count = count;
}

@Override
public Order clone() {
try {
return (Order) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}

public String toString() {
return "Order{product=" + product + ", count=" + count + "}";
}
}

// 使用
public class Main {
public static void main(String[] args) {
Order template = new Order("iPhone", 1);

// 以 template 为模子克隆
Order order1 = template.clone();
order1.setCount(10);

Order order2 = template.clone();
order2.setCount(20);

System.out.println(template);
System.out.println(order1);
System.out.println(order2);
}
}

核心:以现有对象为模子复制新对象。
适合创建成本高、或需要保留母版再改副本的场景。

注意:Java 的 clone() 是浅拷贝,对象里有引用类型时要小心深拷贝。


5. Singleton 单例

全局只有一个实例。

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 ConfigManager {
// 1. 私有静态实例
private static volatile ConfigManager instance;

// 2. 私有构造,禁止外部 new
private ConfigManager() {
}

// 3. 对外提供唯一入口,双重检查锁
public static ConfigManager getInstance() {
if (instance == null) {
synchronized (ConfigManager.class) {
if (instance == null) {
instance = new ConfigManager();
}
}
}
return instance;
}

public void load() {
System.out.println("加载配置");
}
}

// 使用
public class Main {
public static void main(String[] args) {
ConfigManager c1 = ConfigManager.getInstance();
ConfigManager c2 = ConfigManager.getInstance();

c1.load();
System.out.println(c1 == c2); // true
}
}

核心:私有构造 + 静态实例 + 全局访问点。

更简单的写法:

1
2
3
4
5
6
7
enum ConfigManager {
INSTANCE;

public void load() {
System.out.println("加载配置");
}
}

枚举单例天然防反射、防序列化破坏,工程里更推荐。

结构型模式

结构型模式都在解决“对象怎么组合、怎么复用、怎么控制访问”。

模式 核心
Adapter 转接口
Bridge 拆维度
Composite 树形统一
Decorator 包一层加功能
Facade 统一入口
Flyweight 共享对象省内存
Proxy 控制访问

1. Adapter 适配器

接口不兼容,加个转接头。

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
// 老系统接口
interface OldPrinter {
void printOld(String text);
}

class OldPrinterImpl implements OldPrinter {
public void printOld(String text) {
System.out.println("老打印机:" + text);
}
}

// 新系统期望的接口
interface NewPrinter {
void print(String text);
}

// 适配器:把老接口转成新接口
class PrinterAdapter implements NewPrinter {
private OldPrinter oldPrinter;

public PrinterAdapter(OldPrinter oldPrinter) {
this.oldPrinter = oldPrinter;
}

public void print(String text) {
oldPrinter.printOld(text);
}
}

// 使用
public class Main {
public static void main(String[] args) {
NewPrinter printer = new PrinterAdapter(new OldPrinterImpl());
printer.print("Hello");
}
}

核心:不改老代码,包一层转接口。


2. Bridge 桥接

抽象和实现分离,两个维度独立扩展。

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
56
57
58
// 实现维度:颜色
interface Color {
String fill();
}

class Red implements Color {
public String fill() {
return "红色";
}
}

class Blue implements Color {
public String fill() {
return "蓝色";
}
}

// 抽象维度:形状
abstract class Shape {
protected Color color;

public Shape(Color color) {
this.color = color;
}

abstract void draw();
}

class Circle extends Shape {
public Circle(Color color) {
super(color);
}

public void draw() {
System.out.println("画一个 " + color.fill() + " 的圆");
}
}

class Square extends Shape {
public Square(Color color) {
super(color);
}

public void draw() {
System.out.println("画一个 " + color.fill() + " 的方形");
}
}

// 使用
public class Main {
public static void main(String[] args) {
Shape redCircle = new Circle(new Red());
Shape blueSquare = new Square(new Blue());

redCircle.draw();
blueSquare.draw();
}
}

核心:形状和颜色各自扩展,不搞类爆炸。


3. Composite 组合

树形结构,叶子/容器统一处理。

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
import java.util.ArrayList;
import java.util.List;

// 统一组件
interface FileSystemNode {
void show(int depth);
}

// 叶子:文件
class FileNode implements FileSystemNode {
private String name;

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

public void show(int depth) {
System.out.println(" ".repeat(depth) + "文件:" + name);
}
}

// 容器:文件夹
class FolderNode implements FileSystemNode {
private String name;
private List<FileSystemNode> children = new ArrayList<>();

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

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

public void show(int depth) {
System.out.println(" ".repeat(depth) + "文件夹:" + name);
for (FileSystemNode child : children) {
child.show(depth + 1);
}
}
}

// 使用
public class Main {
public static void main(String[] args) {
FolderNode root = new FolderNode("root");
FolderNode docs = new FolderNode("docs");
docs.add(new FileNode("a.txt"));
docs.add(new FileNode("b.txt"));
root.add(docs);
root.add(new FileNode("readme.md"));

root.show(0);
}
}

核心:文件和文件夹统一当节点处理,递归调用。


4. Decorator 装饰

不改进原类,包一层加功能。

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
56
57
58
59
60
61
62
63
// 组件接口
interface Coffee {
String desc();
double cost();
}

// 基础实现
class SimpleCoffee implements Coffee {
public String desc() {
return "普通咖啡";
}

public double cost() {
return 10;
}
}

// 装饰器基类
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;

public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}

// 具体装饰:加奶
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}

public String desc() {
return coffee.desc() + " + 奶";
}

public double cost() {
return coffee.cost() + 2;
}
}

// 具体装饰:加糖
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}

public String desc() {
return coffee.desc() + " + 糖";
}

public double cost() {
return coffee.cost() + 1;
}
}

// 使用
public class Main {
public static void main(String[] args) {
Coffee coffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
System.out.println(coffee.desc() + ",价格:" + coffee.cost());
}
}

核心:一层层包装,动态加功能,不改原类。


5. Facade 外观

给子系统一个统一入口。

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
// 子系统 A
class CPU {
void start() {
System.out.println("CPU 启动");
}
}

// 子系统 B
class Memory {
void load() {
System.out.println("内存加载");
}
}

// 子系统 C
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("电脑启动完成");
}
}

// 使用
public class Main {
public static void main(String[] args) {
new ComputerFacade().start();
}
}

核心:封装复杂调用,对外只给一个简单接口。


6. Flyweight 享元

共享细粒度对象,省内存。

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
import java.util.HashMap;
import java.util.Map;

// 享元对象:棋子颜色
class ChessPiece {
private String color;

public ChessPiece(String color) {
this.color = color;
}

public void put(int x, int y) {
System.out.println(color + " 棋子放在 (" + x + "," + y + ")");
}
}

// 享元工厂:缓存颜色对象
class ChessPieceFactory {
private static Map<String, ChessPiece> pool = new HashMap<>();

public static ChessPiece get(String color) {
if (!pool.containsKey(color)) {
pool.put(color, new ChessPiece(color));
}
return pool.get(color);
}
}

// 使用
public class Main {
public static void main(String[] args) {
ChessPiece black1 = ChessPieceFactory.get("黑");
ChessPiece black2 = ChessPieceFactory.get("黑");
ChessPiece white = ChessPieceFactory.get("白");

black1.put(1, 1);
black2.put(2, 2);
white.put(3, 3);

System.out.println(black1 == black2); // true
}
}

核心:内部状态共享,外部状态由调用方传入。


7. Proxy 代理

替原对象控制访问。

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
// 接口
interface Image {
void display();
}

// 真实对象
class RealImage implements Image {
private String filename;

public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}

private void loadFromDisk() {
System.out.println("加载图片:" + filename);
}

public void display() {
System.out.println("显示图片:" + filename);
}
}

// 代理:懒加载
class ImageProxy implements Image {
private String filename;
private RealImage realImage;

public ImageProxy(String filename) {
this.filename = filename;
}

public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}

// 使用
public class Main {
public static void main(String[] args) {
Image image = new ImageProxy("big.jpg");
System.out.println("第一次调用");
image.display();
System.out.println("第二次调用");
image.display();
}
}

核心:代理控制对真实对象的访问,可加懒加载、权限、日志。

行为型模式

行为型模式都在解决“对象之间怎么交互、职责怎么分配、变化怎么隔离”。

模式 核心
Interpreter 语法变对象树,递归解释
Template Method 父类定流程,子类填步骤
Chain of Responsibility 请求沿链传,能处理就停
Command 请求封装成对象,可撤销可排队
Iterator 统一遍历,不暴露内部结构
Mediator 多对多走中介,变一对多
Memento 状态快照,随时恢复
Observer 一变多通知,发布订阅
State 状态决定行为,自动迁移
Strategy 算法族可互换,运行时切换
Visitor 数据与操作分离,加操作不改原类

1. Interpreter 解释器

解释自定义语法/表达式。

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
56
57
58
// 抽象表达式
interface Expression {
int interpret();
}

// 终结符:数字
class Number implements Expression {
private int value;

public Number(int value) {
this.value = value;
}

public int interpret() {
return value;
}
}

// 非终结符:加法
class Add implements Expression {
private Expression left;
private Expression right;

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

public int interpret() {
return left.interpret() + right.interpret();
}
}

// 非终结符:减法
class Sub implements Expression {
private Expression left;
private Expression right;

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

public int interpret() {
return left.interpret() - right.interpret();
}
}

// 使用:解释 10 + 5 - 3
public class Main {
public static void main(String[] args) {
Expression expr = new Sub(
new Add(new Number(10), new Number(5)),
new Number(3)
);
System.out.println(expr.interpret()); // 12
}
}

核心:把语法规则抽象成对象树,递归解释。


2. Template Method 模板方法

父类定骨架,子类填步骤。

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
abstract class Game {
// 模板方法:定义骨架,final 防子类改流程
public final void play() {
init();
start();
end();
}

protected abstract void init();
protected abstract void start();
protected abstract void end();
}

class Football extends Game {
protected void init() {
System.out.println("足球:热身");
}
protected void start() {
System.out.println("足球:开球");
}
protected void end() {
System.out.println("足球:终场");
}
}

class Basketball extends Game {
protected void init() {
System.out.println("篮球:热身");
}
protected void start() {
System.out.println("篮球:跳球");
}
protected void end() {
System.out.println("篮球:终场");
}
}

// 使用
public class Main {
public static void main(String[] args) {
Game game = new Football();
game.play();
}
}

核心:流程父类定,步骤子类填。
框架里的钩子方法就是这个套路。


3. Chain of Responsibility 责任链

请求沿链传,能处理就处理,否则下一个。

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
56
57
// 抽象处理者
abstract class Handler {
protected Handler next;

public Handler setNext(Handler next) {
this.next = next;
return next;
}

public abstract void handle(int level);
}

// 具体处理者:组长,能处理 level <= 1
class TeamLeader extends Handler {
public void handle(int level) {
if (level <= 1) {
System.out.println("组长审批通过");
} else if (next != null) {
next.handle(level);
}
}
}

// 具体处理者:经理,能处理 level <= 2
class Manager extends Handler {
public void handle(int level) {
if (level <= 2) {
System.out.println("经理审批通过");
} else if (next != null) {
next.handle(level);
}
}
}

// 具体处理者:总监,能处理 level <= 3
class Director extends Handler {
public void handle(int level) {
if (level <= 3) {
System.out.println("总监审批通过");
} else {
System.out.println("无人能审批");
}
}
}

// 使用
public class Main {
public static void main(String[] args) {
Handler chain = new TeamLeader();
chain.setNext(new Manager()).setNext(new Director());

chain.handle(1);
chain.handle(2);
chain.handle(3);
chain.handle(4);
}
}

核心:链式传递,谁合适谁处理,可中断可传递。


4. Command 命令

把请求封装成对象。

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
56
57
58
59
60
61
62
// 命令接口
interface Command {
void execute();
void undo();
}

// 接收者
class Light {
public void on() {
System.out.println("灯打开");
}

public void off() {
System.out.println("灯关闭");
}
}

// 具体命令:开灯
class LightOnCommand implements Command {
private Light light;

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

public void execute() {
light.on();
}

public void undo() {
light.off();
}
}

// 调用者
class RemoteControl {
private Command command;

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

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

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

// 使用
public class Main {
public static void main(String[] args) {
Light light = new Light();
RemoteControl remote = new RemoteControl();

remote.setCommand(new LightOnCommand(light));
remote.press();
remote.pressUndo();
}
}

核心:请求变成对象,可存、可排队、可撤销、可记录日志。


5. Iterator 迭代器

统一遍历集合。

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
import java.util.Iterator;
import java.util.NoSuchElementException;

class MyList<T> implements Iterable<T> {
private Object[] data = new Object[10];
private int size = 0;

public void add(T item) {
data[size++] = item;
}

public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;

public boolean hasNext() {
return index < size;
}

@SuppressWarnings("unchecked")
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return (T) data[index++];
}
};
}
}

// 使用
public class Main {
public static void main(String[] args) {
MyList<String> list = new MyList<>();
list.add("A");
list.add("B");
list.add("C");

for (String s : list) {
System.out.println(s);
}
}
}

核心:遍历逻辑从集合里抽出来,调用方不关心内部结构。


6. Mediator 中介者

对象不直接通信,走中介。
中介者模式是迪米特法则(Law of Demeter, LoD)(最少知识原则(Least Knowledge Principle, LKP))典型应用 。

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
56
import java.util.ArrayList;
import java.util.List;

// 中介者
class ChatRoom {
private List<User> users = new ArrayList<>();

public void join(User user) {
users.add(user);
}

public void send(String message, User from) {
for (User user : users) {
if (user != from) {
user.receive(message, from.getName());
}
}
}
}

// 同事类
class User {
private String name;
private ChatRoom room;

public User(String name, ChatRoom room) {
this.name = name;
this.room = room;
room.join(this);
}

public String getName() {
return name;
}

public void send(String message) {
System.out.println(name + " 发送:" + message);
room.send(message, this);
}

public void receive(String message, String from) {
System.out.println(name + " 收到来自 " + from + " 的消息:" + message);
}
}

// 使用
public class Main {
public static void main(String[] args) {
ChatRoom room = new ChatRoom();
User a = new User("Alice", room);
User b = new User("Bob", room);
User c = new User("Carol", room);

a.send("大家好");
}
}

核心:多对多通信变一对多,同事类之间不直接引用。


7. Memento 备忘录

保存状态,之后恢复。

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 备忘录:只存状态
class Memento {
private String state;

public Memento(String state) {
this.state = state;
}

public String getState() {
return state;
}
}

// 原发器:需要保存状态的对象
class Editor {
private String content = "";

public void type(String words) {
content += words;
}

public String getContent() {
return content;
}

public Memento save() {
return new Memento(content);
}

public void restore(Memento memento) {
content = memento.getState();
}
}

// 管理者:保存备忘录
class History {
private java.util.Stack<Memento> stack = new java.util.Stack<>();

public void push(Memento memento) {
stack.push(memento);
}

public Memento pop() {
return stack.pop();
}
}

// 使用
public class Main {
public static void main(String[] args) {
Editor editor = new Editor();
History history = new History();

editor.type("Hello");
history.push(editor.save());

editor.type(" World");
history.push(editor.save());

editor.type("!!!");
System.out.println("当前:" + editor.getContent());

editor.restore(history.pop());
System.out.println("撤销一次:" + editor.getContent());

editor.restore(history.pop());
System.out.println("撤销两次:" + editor.getContent());
}
}

核心:状态快照存到外部,需要时恢复,不破坏封装。


8. Observer 观察者

一变多通知,发布订阅。

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
import java.util.ArrayList;
import java.util.List;

// 观察者接口
interface Observer {
void update(String message);
}

// 被观察者
class NewsAgency {
private List<Observer> observers = new ArrayList<>();

public void subscribe(Observer observer) {
observers.add(observer);
}

public void unsubscribe(Observer observer) {
observers.remove(observer);
}

public void publish(String news) {
for (Observer observer : observers) {
observer.update(news);
}
}
}

// 具体观察者
class User implements Observer {
private String name;

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

public void update(String message) {
System.out.println(name + " 收到新闻:" + message);
}
}

// 使用
public class Main {
public static void main(String[] args) {
NewsAgency agency = new NewsAgency();

User a = new User("Alice");
User b = new User("Bob");

agency.subscribe(a);
agency.subscribe(b);

agency.publish("今天有大事");
}
}

核心:发布者不关心谁订阅,订阅者自动收到通知。


9. State 状态

状态决定行为,状态可迁移。

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
56
57
58
59
// 状态接口
interface State {
void handle(Order order);
}

// 具体状态:待支付
class PendingState implements State {
public void handle(Order order) {
System.out.println("待支付:去支付");
order.setState(new PaidState());
}
}

// 具体状态:已支付
class PaidState implements State {
public void handle(Order order) {
System.out.println("已支付:去发货");
order.setState(new ShippedState());
}
}

// 具体状态:已发货
class ShippedState implements State {
public void handle(Order order) {
System.out.println("已发货:已完成");
order.setState(new DoneState());
}
}

// 具体状态:已完成
class DoneState implements State {
public void handle(Order order) {
System.out.println("已完成:不能再操作");
}
}

// 上下文
class Order {
private State state = new PendingState();

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

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

// 使用
public class Main {
public static void main(String[] args) {
Order order = new Order();
order.next();
order.next();
order.next();
order.next();
}
}

核心:状态自己决定下一步,避免一堆 if-else。


10. Strategy 策略

算法族可互换。

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
56
57
// 策略接口
interface SortStrategy {
void sort(int[] arr);
}

// 具体策略:冒泡
class BubbleSort implements SortStrategy {
public void sort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
}

// 具体策略:快速
class QuickSort implements SortStrategy {
public void sort(int[] arr) {
java.util.Arrays.sort(arr);
}
}

// 上下文
class Sorter {
private SortStrategy strategy;

public Sorter(SortStrategy strategy) {
this.strategy = strategy;
}

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

public void sort(int[] arr) {
strategy.sort(arr);
}
}

// 使用
public class Main {
public static void main(String[] args) {
int[] arr = {5, 2, 8, 1};

Sorter sorter = new Sorter(new BubbleSort());
sorter.sort(arr);
System.out.println(java.util.Arrays.toString(arr));

sorter.setStrategy(new QuickSort());
sorter.sort(arr);
}
}

核心:算法独立成类,运行时可换。
和 State 的区别:Strategy 是外部主动换算法;State 是状态自己决定行为。


11. 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import java.util.ArrayList;
import java.util.List;

// 访问者接口
interface Visitor {
void visit(FileNode file);
void visit(FolderNode folder);
}

// 元素接口
interface Node {
void accept(Visitor visitor);
}

// 具体元素:文件
class FileNode implements Node {
private String name;
private int size;

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

public String getName() {
return name;
}

public int getSize() {
return size;
}

public void accept(Visitor visitor) {
visitor.visit(this);
}
}

// 具体元素:文件夹
class FolderNode implements Node {
private String name;
private List<Node> children = new ArrayList<>();

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

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

public String getName() {
return name;
}

public List<Node> getChildren() {
return children;
}

public void accept(Visitor visitor) {
visitor.visit(this);
}
}

// 具体访问者:统计大小
class SizeVisitor implements Visitor {
private int total = 0;

public void visit(FileNode file) {
total += file.getSize();
}

public void visit(FolderNode folder) {
for (Node child : folder.getChildren()) {
child.accept(this);
}
}

public int getTotal() {
return total;
}
}

// 使用
public class Main {
public static void main(String[] args) {
FolderNode root = new FolderNode("root");
FolderNode docs = new FolderNode("docs");
docs.add(new FileNode("a.txt", 10));
docs.add(new FileNode("b.txt", 20));
root.add(docs);
root.add(new FileNode("readme.md", 5));

SizeVisitor visitor = new SizeVisitor();
root.accept(visitor);
System.out.println("总大小:" + visitor.getTotal());
}
}

核心:数据结构和操作分离,加新操作不改元素类。

常见问题

Q1:23 种设计模式怎么分类?
A:

  • 创建型:管“对象怎么来”。
  • 结构型:管“对象怎么拼”。
  • 行为型:管“对象怎么交互”。

Q2:创建型五个怎么区分?
A:

  • Factory Method:父类定创建接口,子类决定 new 哪个。
  • Abstract Factory:创建一族相关对象。
  • Builder:分步构造复杂对象。
  • Prototype:克隆现成对象。
  • Singleton:全局唯一实例。

一句话:工厂管“造哪个”,抽象工厂管“造一族”,Builder 管“怎么一步步造”,Prototype 管“照着造”,Singleton 管“只造一个”。


Q3:抽象工厂和工厂方法差不多?
A:像,但不一样。

  • 工厂方法:创建一个产品。
  • 抽象工厂:创建一族配套产品。
  • 抽象工厂里每个 createX() 可以看作一个工厂方法。
  • 但抽象工厂多了“产品族必须配套”的约束。

Q4:结构型七个怎么区分?
A:

  • Adapter:转接口。
  • Bridge:拆维度,用组合代替继承。
  • Composite:树形结构,叶子/容器统一处理。
  • Decorator:包一层加功能。
  • Facade:统一入口,简化调用。
  • Flyweight:共享对象,省内存。
  • Proxy:控制对真实对象的访问。

Q5:Bridge 就是关联关系吗?
A:结构上常用关联/组合实现,但有关联不等于 Bridge。
Bridge 的关键是:两个独立变化的维度,各自扩展,避免继承爆炸。


Q6:Composite 为什么好用?
A:它把文件和文件夹统一成同一种节点。
调用方不用区分叶子和容器,递归处理即可。菜单树、组织架构、UI 容器都是这个套路。


Q7:Facade 和 Abstract Factory 能坐一桌吗?
A:都封装,但不是一类。

  • Facade:结构型,简化调用,给子系统统一入口。
  • Abstract Factory:创建型,封装创建一族对象。

Q8:Proxy 懒加载怎么理解?
A:代理层先暂存参数或条件,不急着创建真实对象。
第一次真正需要时才创建。核心就是:先记账,后干活。


Q9:Flyweight 工厂静态方法为什么能 new 非静态类?像单例吗?
A:static 方法属于类,可以 new 任意对象、访问静态成员。
Flyweight 按 key 共享多个对象;Singleton 全局只允许一个实例。像,但不一样。


Q10:行为型怎么记?
A:

  • Interpreter:语法变对象树,递归解释。
  • Template Method:父类定流程,子类填步骤。
  • Chain of Responsibility:请求沿链传。
  • Command:请求封装成对象。
  • Iterator:统一遍历。
  • Mediator:多对多走中介。
  • Memento:状态快照。
  • Observer:发布订阅。
  • State:状态决定行为。
  • Strategy:算法族可互换。
  • Visitor:数据与操作分离。

Q11:解释器一定要用栈吗?像编译原理、状态机吗?
A:不一定用栈,递归或对象树也可以。
它像编译原理里“语法树 + 求值”那一小部分,不是完整编译器。
本质不是状态机,核心是语法树递归求值。


Q12:责任链里 next 没定义为什么能用?
A:next 定义在父类 Handler 里,通常是 protected 字段。
子类继承后可以直接访问。责任链本质就是手动拼了一个单向链表。


Q13:Command 不就是函数吗?
A:很多 Command 可以用函数、Runnable、Consumer 替代。
Command 多出来的是“对象化”:可排队、可撤销、可记录日志、可组合成宏命令。


Q14:增强 for 循环编译后会生成迭代器吗?
A:实现 Iterable 的集合会生成 Iterator。
数组的增强 for 编译后是普通下标循环。


Q15:群聊是不是 Mediator 实现的?
A:核心模型就是中介者。用户不直接互发,统一发给聊天室,聊天室转发。
真实群聊还涉及持久化、离线消息、多设备同步、消息顺序等,工程更复杂。


Q16:Memento 不就是栈吗?哈希表也可以?
A:Memento 是“状态快照对象”。
栈、HashMap 是存放快照的容器。
撤销常用 Stack<Memento>,存档可用 Map<String, Memento>。


Q17:Observer 名字绕,能叫分发节点吗?
A:可以。
Subject 就是发布者、事件源、分发节点。
Observer 就是订阅者、接收者、监听器。


Q18:Visitor 为什么绕?什么叫双重分派?
A:Java 是单分派,直接 visitor.visit(node) 不能按运行时类型走。
所以绕两次:

  1. node.accept(visitor) 确定元素类型。
  2. visitor.visit(this) 确定访问者类型。

好处:加新操作不改节点。
坏处:加新节点类型要改所有 Visitor。


Q19:Visitor 不适合 Java?工具类更好?
A:业务代码里,工具类静态方法通常更直白。
Visitor 适合:操作多、节点稳定、不想写 instanceof 的场景,比如编译器、解析器、代码分析。
Java 单分派导致 Visitor 绕;其他语言用动态分派、多分派、密封类 + 模式匹配替代。


Q20:一段代码能同时是多个模式吗?
A:能。模式是视角,不是互斥标签。
一个类可以同时是 Singleton、Facade,内部又用 Factory 和 Strategy。


Q21:Visitor 是不是语言补丁?
A:是。
它本质是在单分派语言里模拟多分派。
语言进步后,很多补丁被固化进语法,比如 Kotlin 的 sealed + when、Rust 的 enum + match。


Q22:Facade 门面模式是什么?
A:门面模式就是外观模式,同一个东西。
核心:对外给统一、稳定的高层接口,把内部复杂调用包起来。
平台对多产品接口耦合时,平台侧加 Facade 收口,产品只依赖 Facade。


Q23:产品要利用门面模式重构,谁重构?平台还是产品?
A:被依赖的公共底座重构,通常是平台侧。
思路:平台加 Facade,对外稳定,对内吸收变化。
产品只依赖 Facade,不再因平台内部接口变化而联动修改。
再进一步,可演进为插件化。