系统梳理 GoF 23 种设计模式,结合前端与 Node.js 实战代码,配 UML 类图与时序示意图辅助理解。
目录
什么是设计模式
创建型模式(5 种)
结构型模式(7 种)
行为型模式(11 种)
速查总表
一、什么是设计模式 设计模式(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) 意图 :保证一个类只有一个实例,并提供全局访问点。
Singleton
- instance
- constructor()
+ getInstance()
+ operation()
creates
场景 :全局状态管理(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);
2.2 工厂方法模式(Factory Method) 意图 :定义创建对象的接口,让子类决定实例化哪个类。
abstract
Transport
+createVehicle() +deliver()
RoadTransport
+createVehicle()
SeaTransport
+createVehicle()
interface
Vehicle
+move()
Truck
+move()
Ship
+move()
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 ();
2.3 抽象工厂模式(Abstract Factory) 意图 :提供一个接口,用于创建一系列相关或依赖 对象,而不指定具体类。
interface
UIFactory
+createButton() +createInput()
LightThemeFactory
+createButton() +createInput()
DarkThemeFactory
+createButton() +createInput()
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);
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 ();
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 (); bob.greet ();
三、结构型模式(7 种) 3.1 适配器模式(Adapter) 意图 :将一个接口转换成调用方期望的另一个接口——解决不兼容问题。
interface
Target
+request()
Adapter
-adaptee
+request()
Adaptee
+specificRequest()
场景 :旧接口兼容、第三方 SDK 包装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class OldLogger { log (msg ) { console .log ('[OLD]' , msg); } } 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' );
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 (); new Square (canvasRenderer).draw ();
3.3 组合模式(Composite) 意图 :将对象组织成树形结构,使单个对象和组合对象的使用方式一致。
场景 :文件系统、组件树、权限树。
interface
Component
+getName() +getSize()
File
+getName() +getSize()
Folder
+add(child) ...
contains
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 ();
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 );
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 ();const chars = Array .from ('Hello World x1000' ).map (ch => ({ char : ch, style : pool.get ('Arial' , 14 , '#333' ), })); console .log (pool.size ());
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 ; user.email = 'a@b.com' ;
四、行为型模式(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' }); chain.handle ({ token : 'tok' , role : 'guest' }); chain.handle ({});
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 (); ed.undo (); ed.value ();
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' ); interpret ('10 - 3 + 2' );
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 )]; for (const n of new Range (0 , 5 )) console .log (n);
意图 :用中介对象封装一组对象的交互,减少对象间直接引用。
场景 :聊天室、事件总线、机场调度。
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' });
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 (); form.restore (); form.get ();
4.7 观察者模式(Observer) 意图 :对象间一对多依赖,当一个对象状态变化时,自动通知所有依赖对象。
场景 :Vue/React 响应式、DOM 事件、数据绑定。
Subject
ObserverA
ObserverB
attach(A)
attach(B)
setState
update
update
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 );
4.8 状态模式(State) 意图 :允许对象在内部状态改变时改变其行为,使对象看起来修改了它的类。
场景 :订单状态机、红绿灯、播放器状态。
Idle
Loading
Success
Error
fetch
resolve
reject
reset
reset
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' ); m.dispatch ('resolve' , { id : 1 }); m.dispatch ('reset' );
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 ]); new Sorter ('quick' ).sort ([3 , 1 , 4 , 1 , 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); new JSON Exporter().export (rows);
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)} )` , }; const ast = new MulNode (new AddNode (new NumberNode (2 ), new NumberNode (3 )), new NumberNode (4 ));ast.accept (evalVisitor); ast.accept (printVisitor);
五、速查总表
分类
模式
核心意图
常见场景
创建型
单例
唯一实例
Store、Logger、Config
创建型
工厂方法
子类决定实例化
组件工厂、解析器
创建型
抽象工厂
相关对象族
主题 UI、跨平台组件
创建型
建造者
分步构建复杂对象
QueryBuilder、配置
创建型
原型
克隆已有实例
对象池、深拷贝
结构型
适配器
接口转换
旧库兼容、SDK 包装
结构型
桥接
抽象与实现分离
渲染引擎、驱动层
结构型
组合
树形统一接口
文件系统、组件树
结构型
装饰器
动态扩展功能
中间件、HOC、缓存
结构型
外观
统一高层接口
SDK 初始化入口
结构型
享元
共享细粒度对象
字符渲染、粒子池
结构型
代理
控制对象访问
懒加载、权限、校验
行为型
责任链
链式处理请求
中间件管道、审批流
行为型
命令
操作对象化
撤销/重做、任务队列
行为型
解释器
语言文法解析
模板引擎、DSL
行为型
迭代器
统一遍历接口
for…of、Generator
行为型
中介者
集中对象交互
EventBus、聊天室
行为型
备忘录
状态快照/恢复
撤销历史、草稿
行为型
观察者
自动通知订阅者
Vue 响应式、事件
行为型
状态
状态驱动行为
订单流、状态机
行为型
策略
算法可互换
支付、排序、折扣
行为型
模板方法
固定骨架/可变步骤
导出流程、生命周期
行为型
访问者
不改类增加操作
AST 遍历、报表