设计模式全解:原理、UML 与项目实战

系统梳理 GoF 23 种设计模式,结合前端与 Node.js 实战代码,配 UML 类图与时序示意图辅助理解。


目录

  1. 什么是设计模式
  2. 创建型模式(5 种)
  3. 结构型模式(7 种)
  4. 行为型模式(11 种)
  5. 速查总表

一、什么是设计模式

设计模式(Design Pattern) 是软件工程中反复出现问题的通用可复用解决方案。1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides(即「四人组 GoF」)在《设计模式:可复用面向对象软件的基础》中总结了 23 种经典模式,按目的分为三类:

1
2
3
创建型(Creational)  ——  解决对象的创建问题
结构型(Structural) —— 解决类/对象的组合问题
行为型(Behavioral) —— 解决对象间职责与通信问题

设计原则(SOLID)

原则 说明
S ingle Responsibility 一个类只做一件事
O pen/Closed 对扩展开放,对修改关闭
L iskov Substitution 子类可替换父类而不破坏程序
I nterface Segregation 接口最小化,不强迫实现不需要的方法
D ependency Inversion 依赖抽象,而非具体实现

二、创建型模式(5 种)

2.1 单例模式(Singleton)

意图:保证一个类只有一个实例,并提供全局访问点。

场景:全局状态管理(Store)、日志记录器、配置管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Store {
static #instance = null;

#state = {};

static getInstance() {
if (!Store.#instance) {
Store.#instance = new Store();
}
return Store.#instance;
}

getState() { return this.#state; }
setState(patch) { Object.assign(this.#state, patch); }
}

const a = Store.getInstance();
const b = Store.getInstance();
console.log(a === b); // true

2.2 工厂方法模式(Factory Method)

意图:定义创建对象的接口,让子类决定实例化哪个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Button {
render() { throw new Error('abstract'); }
}
class PrimaryButton extends Button {
render() { return `<button class="btn-primary">OK</button>`; }
}
class DangerButton extends Button {
render() { return `<button class="btn-danger">Delete</button>`; }
}

function createButton(type) {
const map = { primary: PrimaryButton, danger: DangerButton };
const Ctor = map[type];
if (!Ctor) throw new Error(`Unknown type: ${type}`);
return new Ctor();
}

createButton('primary').render(); // <button class="btn-primary">OK</button>

2.3 抽象工厂模式(Abstract Factory)

意图:提供一个接口,用于创建一系列相关或依赖对象,而不指定具体类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const lightFactory = {
createButton: () => ({ type: 'button', theme: 'light' }),
createInput: () => ({ type: 'input', theme: 'light' }),
};
const darkFactory = {
createButton: () => ({ type: 'button', theme: 'dark' }),
createInput: () => ({ type: 'input', theme: 'dark' }),
};

function buildUI(factory) {
return {
button: factory.createButton(),
input: factory.createInput(),
};
}
buildUI(darkFactory); // { button: {theme:'dark'}, input: {theme:'dark'} }

2.4 建造者模式(Builder)

意图:将复杂对象的构造表示分离,同一构建过程可创建不同表示。

场景:SQL 查询构建、配置对象构造、链式调用 API。

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
class QueryBuilder {
#table = '';
#conditions = [];
#fields = ['*'];
#limit = null;

from(table) { this.#table = table; return this; }
select(...f) { this.#fields = f; return this; }
where(cond) { this.#conditions.push(cond); return this; }
limit(n) { this.#limit = n; return this; }

build() {
let sql = `SELECT ${this.#fields.join(', ')} FROM ${this.#table}`;
if (this.#conditions.length) sql += ` WHERE ${this.#conditions.join(' AND ')}`;
if (this.#limit !== null) sql += ` LIMIT ${this.#limit}`;
return sql;
}
}

new QueryBuilder()
.from('users')
.select('id', 'name')
.where('age > 18')
.where('active = 1')
.limit(10)
.build();
// SELECT id, name FROM users WHERE age > 18 AND active = 1 LIMIT 10

2.5 原型模式(Prototype)

意图:通过复制(克隆)已有实例来创建新对象,而不是通过 new

1
2
3
4
5
6
7
8
9
10
11
12
13
const userProto = {
greet() { return `Hi, I'm ${this.name}`; },
clone() { return Object.create(this); },
};

const alice = Object.create(userProto);
alice.name = 'Alice';

const bob = alice.clone();
bob.name = 'Bob';

alice.greet(); // Hi, I'm Alice
bob.greet(); // Hi, I'm Bob

三、结构型模式(7 种)

3.1 适配器模式(Adapter)

意图:将一个接口转换成调用方期望的另一个接口——解决不兼容问题。

场景:旧接口兼容、第三方 SDK 包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 旧日志库,接口为 log(msg)
class OldLogger {
log(msg) { console.log('[OLD]', msg); }
}

// 新系统要求 info / warn / error 三个方法
class LoggerAdapter {
constructor(logger) { this.logger = logger; }
info(msg) { this.logger.log(`INFO ${msg}`); }
warn(msg) { this.logger.log(`WARN ${msg}`); }
error(msg) { this.logger.log(`ERROR ${msg}`); }
}

const logger = new LoggerAdapter(new OldLogger());
logger.info('server started'); // [OLD] INFO server started

3.2 桥接模式(Bridge)

意图:将抽象部分与实现部分分离,使二者可以独立变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 实现部分:渲染引擎
const canvasRenderer = { draw: (shape) => `Canvas: draw ${shape}` };
const svgRenderer = { draw: (shape) => `SVG: draw ${shape}` };

// 抽象部分:形状
class Shape {
constructor(renderer) { this.renderer = renderer; }
}
class Circle extends Shape {
draw() { return this.renderer.draw('circle'); }
}
class Square extends Shape {
draw() { return this.renderer.draw('square'); }
}

new Circle(svgRenderer).draw(); // SVG: draw circle
new Square(canvasRenderer).draw(); // Canvas: draw square

3.3 组合模式(Composite)

意图:将对象组织成树形结构,使单个对象和组合对象的使用方式一致。

场景:文件系统、组件树、权限树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class File {
constructor(name, size) { this.name = name; this.size = size; }
getSize() { return this.size; }
}
class Folder {
constructor(name) { this.name = name; this.children = []; }
add(child) { this.children.push(child); return this; }
getSize() { return this.children.reduce((s, c) => s + c.getSize(), 0); }
}

const root = new Folder('root')
.add(new File('a.js', 10))
.add(
new Folder('src')
.add(new File('index.js', 20))
.add(new File('utils.js', 15))
);

root.getSize(); // 45

3.4 装饰器模式(Decorator)

意图:在不改变原对象的前提下,动态地为其添加功能。

场景:中间件(Koa/Express)、日志增强、缓存包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function withLogging(fn) {
return function (...args) {
console.log(`[CALL] ${fn.name}(${args})`);
const result = fn.apply(this, args);
console.log(`[RET] ${result}`);
return result;
};
}

function add(a, b) { return a + b; }
const loggedAdd = withLogging(add);
loggedAdd(2, 3);
// [CALL] add(2,3)
// [RET] 5

3.5 外观模式(Facade)

意图:为子系统提供统一的高层接口,隐藏内部复杂性。

场景:SDK 封装、初始化流程统一入口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AuthService   { login(u, p)    { return { token: 'tok_' + u }; } }
class ProfileService { fetch(token) { return { name: 'Alice' }; } }
class LogService { record(action) { console.log('[LOG]', action); } }

class AppFacade {
#auth = new AuthService();
#profile = new ProfileService();
#log = new LogService();

async signIn(username, password) {
const { token } = this.#auth.login(username, password);
const profile = this.#profile.fetch(token);
this.#log.record(`login:${username}`);
return { token, profile };
}
}

new AppFacade().signIn('alice', 'pw');

3.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
class CharStyle {
constructor(font, size, color) {
this.font = font; this.size = size; this.color = color;
}
}

class CharStylePool {
#pool = new Map();
get(font, size, color) {
const key = `${font}-${size}-${color}`;
if (!this.#pool.has(key)) this.#pool.set(key, new CharStyle(font, size, color));
return this.#pool.get(key);
}
size() { return this.#pool.size; }
}

const pool = new CharStylePool();
// 渲染 1000 个字符,只需要极少的 CharStyle 对象
const chars = Array.from('Hello World x1000').map(ch => ({
char: ch,
style: pool.get('Arial', 14, '#333'),
}));
console.log(pool.size()); // 1(所有字符共享同一样式对象)

3.7 代理模式(Proxy)

意图:为另一个对象提供代理以控制对它的访问。

场景:懒加载、权限控制、缓存、数据校验(ES Proxy)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function createValidator(target, rules) {
return new Proxy(target, {
set(obj, prop, value) {
if (rules[prop] && !rules[prop](value)) {
throw new TypeError(`Invalid value for "${prop}": ${value}`);
}
obj[prop] = value;
return true;
},
});
}

const user = createValidator({}, {
age: v => typeof v === 'number' && v >= 0 && v <= 150,
email: v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
});

user.age = 25; // OK
user.email = 'a@b.com'; // OK
// user.age = -1; // TypeError: Invalid value for "age": -1

四、行为型模式(11 种)

4.1 责任链模式(Chain of Responsibility)

意图:将请求沿着处理者链传递,直到某个处理者处理它为止。

场景:中间件管道(Koa)、审批流程、事件冒泡。

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
class Handler {
setNext(handler) { this.next = handler; return handler; }
handle(req) { return this.next?.handle(req); }
}

class AuthHandler extends Handler {
handle(req) {
if (!req.token) return '401 Unauthorized';
return super.handle(req);
}
}
class RoleHandler extends Handler {
handle(req) {
if (req.role !== 'admin') return '403 Forbidden';
return super.handle(req);
}
}
class BusinessHandler extends Handler {
handle(req) { return `200 OK: Hello ${req.user}`; }
}

const chain = new AuthHandler();
chain.setNext(new RoleHandler()).setNext(new BusinessHandler());

chain.handle({ token: 'tok', role: 'admin', user: 'Alice' }); // 200 OK
chain.handle({ token: 'tok', role: 'guest' }); // 403 Forbidden
chain.handle({}); // 401 Unauthorized

4.2 命令模式(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
class TextEditor {
#text = '';
#history = [];

execute(command) {
this.#text = command.execute(this.#text);
this.#history.push(command);
return this;
}
undo() {
const cmd = this.#history.pop();
if (cmd) this.#text = cmd.undo(this.#text);
return this;
}
value() { return this.#text; }
}

const appendCmd = (str) => ({
execute: (t) => t + str,
undo: (t) => t.slice(0, -str.length),
});

const ed = new TextEditor();
ed.execute(appendCmd('Hello')).execute(appendCmd(', World'));
ed.value(); // Hello, World
ed.undo();
ed.value(); // Hello

4.3 解释器模式(Interpreter)

意图:为语言定义文法,并实现解释器来解释该语言的句子。

场景:模板引擎、DSL 解析、规则引擎。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 极简四则运算解释器
function interpret(expr) {
expr = expr.trim();
const addIdx = expr.lastIndexOf('+');
const subIdx = expr.lastIndexOf('-');
const pivot = Math.max(addIdx, subIdx);
if (pivot > 0) {
const left = interpret(expr.slice(0, pivot));
const right = interpret(expr.slice(pivot + 1));
return expr[pivot] === '+' ? left + right : left - right;
}
return parseFloat(expr);
}

interpret('1 + 2 + 3'); // 6
interpret('10 - 3 + 2'); // 9

4.4 迭代器模式(Iterator)

意图:提供顺序访问集合元素的方法,而不暴露其内部表示。

JavaScript 内置迭代器协议(Symbol.iterator)即此模式的语言级实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Range {
constructor(start, end, step = 1) {
this.start = start; this.end = end; this.step = step;
}
[Symbol.iterator]() {
let cur = this.start;
return {
next: () => cur <= this.end
? { value: cur += this.step, done: false }
: { done: true },
};
}
}

[...new Range(1, 10, 2)]; // [3, 5, 7, 9, 11] — 每步 +2
for (const n of new Range(0, 5)) console.log(n); // 1 2 3 4 5

4.5 中介者模式(Mediator)

意图:用中介对象封装一组对象的交互,减少对象间直接引用。

场景:聊天室、事件总线、机场调度。

1
2
3
4
5
6
7
8
9
10
11
12
13
class EventBus {
#listeners = new Map();
on(event, fn) { (this.#listeners.get(event) ?? this.#listeners.set(event, new Set()).get(event)).add(fn); }
off(event, fn) { this.#listeners.get(event)?.delete(fn); }
emit(event, data) { this.#listeners.get(event)?.forEach(fn => fn(data)); }
}

const bus = new EventBus();
bus.on('login', ({ user }) => console.log(`Welcome, ${user}`));
bus.on('login', ({ user }) => console.log(`Log: ${user} logged in`));
bus.emit('login', { user: 'Alice' });
// Welcome, Alice
// Log: Alice logged in

4.6 备忘录模式(Memento)

意图:在不破坏封装的前提下,捕获并外部化对象的内部状态,以便恢复。

场景:撤销/重做、草稿保存、游戏存档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class FormState {
#snapshots = [];
#current = {};

set(patch) { this.#current = { ...this.#current, ...patch }; }
snapshot() { this.#snapshots.push({ ...this.#current }); }
restore() { if (this.#snapshots.length) this.#current = this.#snapshots.pop(); }
get() { return { ...this.#current }; }
}

const form = new FormState();
form.set({ name: 'Alice' });
form.snapshot();
form.set({ name: 'Bob', age: 30 });
form.get(); // { name: 'Bob', age: 30 }
form.restore();
form.get(); // { name: 'Alice' }

4.7 观察者模式(Observer)

意图:对象间一对多依赖,当一个对象状态变化时,自动通知所有依赖对象。

场景:Vue/React 响应式、DOM 事件、数据绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Subject {
#observers = new Set();
#state;

subscribe(fn) { this.#observers.add(fn); }
unsubscribe(fn) { this.#observers.delete(fn); }

setState(val) {
this.#state = val;
this.#observers.forEach(fn => fn(val));
}
getState() { return this.#state; }
}

const counter = new Subject();
counter.subscribe(v => console.log('A sees:', v));
counter.subscribe(v => console.log('B sees:', v));
counter.setState(42);
// A sees: 42
// B sees: 42

4.8 状态模式(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
const states = {
idle: { fetch: (ctx) => ctx.transition('loading') },
loading: {
resolve: (ctx, data) => { ctx.data = data; ctx.transition('success'); },
reject: (ctx, err) => { ctx.error = err; ctx.transition('error'); },
},
success: { reset: (ctx) => ctx.transition('idle') },
error: { reset: (ctx) => ctx.transition('idle') },
};

class AsyncMachine {
#state = 'idle';
data = null;
error = null;

transition(s) { this.#state = s; console.log('→', s); }
dispatch(action, payload) {
const handler = states[this.#state]?.[action];
if (!handler) return console.warn(`No "${action}" in state "${this.#state}"`);
handler(this, payload);
}
getState() { return this.#state; }
}

const m = new AsyncMachine();
m.dispatch('fetch'); // → loading
m.dispatch('resolve', { id: 1 }); // → success
m.dispatch('reset'); // → idle

4.9 策略模式(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
const sortStrategies = {
bubble: (arr) => {
const a = [...arr];
for (let i = 0; i < a.length; i++)
for (let j = 0; j < a.length - i - 1; j++)
if (a[j] > a[j+1]) [a[j], a[j+1]] = [a[j+1], a[j]];
return a;
},
quick: (arr) => {
if (arr.length <= 1) return arr;
const pivot = arr[0];
const left = arr.slice(1).filter(x => x <= pivot);
const right = arr.slice(1).filter(x => x > pivot);
return [...sortStrategies.quick(left), pivot, ...sortStrategies.quick(right)];
},
};

class Sorter {
constructor(strategy = 'quick') { this.strategy = strategy; }
sort(arr) { return sortStrategies[this.strategy](arr); }
}

new Sorter('bubble').sort([3, 1, 4, 1, 5]); // [1, 1, 3, 4, 5]
new Sorter('quick').sort([3, 1, 4, 1, 5]); // [1, 1, 3, 4, 5]

4.10 模板方法模式(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
class DataExporter {
// 模板方法:算法骨架
export(data) {
const processed = this.process(data);
const formatted = this.format(processed);
this.save(formatted);
}

process(data) { return data.filter(Boolean); } // 默认实现
format(data) { throw new Error('abstract'); } // 子类实现
save(content) { console.log('saving:', content.slice(0, 40)); }
}

class CSVExporter extends DataExporter {
format(data) { return data.map(r => Object.values(r).join(',')).join('\n'); }
}
class JSONExporter extends DataExporter {
format(data) { return JSON.stringify(data, null, 2); }
}

const rows = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
new CSVExporter().export(rows); // saving: 1,Alice\n2,Bob
new JSONExporter().export(rows); // saving: [\n {\n "id": 1,

4.11 访问者模式(Visitor)

意图:在不改变元素类的前提下,为其定义作用于这些元素的新操作。

场景:AST 遍历、报表统计、编译器优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class NumberNode  { constructor(v)    { this.value = v; } accept(v) { return v.visitNumber(this); } }
class AddNode { constructor(l, r) { this.left = l; this.right = r; } accept(v) { return v.visitAdd(this); } }
class MulNode { constructor(l, r) { this.left = l; this.right = r; } accept(v) { return v.visitMul(this); } }

// 求值访问者
const evalVisitor = {
visitNumber: (n) => n.value,
visitAdd: (n) => n.left.accept(evalVisitor) + n.right.accept(evalVisitor),
visitMul: (n) => n.left.accept(evalVisitor) * n.right.accept(evalVisitor),
};

// 打印访问者
const printVisitor = {
visitNumber: (n) => `${n.value}`,
visitAdd: (n) => `(${n.left.accept(printVisitor)} + ${n.right.accept(printVisitor)})`,
visitMul: (n) => `(${n.left.accept(printVisitor)} * ${n.right.accept(printVisitor)})`,
};

// (2 + 3) * 4
const ast = new MulNode(new AddNode(new NumberNode(2), new NumberNode(3)), new NumberNode(4));
ast.accept(evalVisitor); // 20
ast.accept(printVisitor); // ((2 + 3) * 4)

五、速查总表

分类 模式 核心意图 常见场景
创建型 单例 唯一实例 Store、Logger、Config
创建型 工厂方法 子类决定实例化 组件工厂、解析器
创建型 抽象工厂 相关对象族 主题 UI、跨平台组件
创建型 建造者 分步构建复杂对象 QueryBuilder、配置
创建型 原型 克隆已有实例 对象池、深拷贝
结构型 适配器 接口转换 旧库兼容、SDK 包装
结构型 桥接 抽象与实现分离 渲染引擎、驱动层
结构型 组合 树形统一接口 文件系统、组件树
结构型 装饰器 动态扩展功能 中间件、HOC、缓存
结构型 外观 统一高层接口 SDK 初始化入口
结构型 享元 共享细粒度对象 字符渲染、粒子池
结构型 代理 控制对象访问 懒加载、权限、校验
行为型 责任链 链式处理请求 中间件管道、审批流
行为型 命令 操作对象化 撤销/重做、任务队列
行为型 解释器 语言文法解析 模板引擎、DSL
行为型 迭代器 统一遍历接口 for…of、Generator
行为型 中介者 集中对象交互 EventBus、聊天室
行为型 备忘录 状态快照/恢复 撤销历史、草稿
行为型 观察者 自动通知订阅者 Vue 响应式、事件
行为型 状态 状态驱动行为 订单流、状态机
行为型 策略 算法可互换 支付、排序、折扣
行为型 模板方法 固定骨架/可变步骤 导出流程、生命周期
行为型 访问者 不改类增加操作 AST 遍历、报表

设计模式全解:原理、UML 与项目实战

https://bitgarden.cn/2020/02/12/Design-Patterns/设计模式全解/

Author

Felix Tao

Posted on

2020-02-12

Updated on

2024-01-01

Licensed under