设计模式文档
设计模式不是八股文,而是前人总结的解决常见问题的最优方案。这篇文章用 JavaScript 实现最常用的设计模式,配代码示例和适用场景。
一、五大设计原则(SOLID)
| 原则 | 含义 |
|---|---|
| 单一职责(SRP) | 一个类只做一件事 |
| 开放封闭(OCP) | 对扩展开放,对修改封闭 |
| 里氏替换(LSP) | 子类必须能替换父类 |
| 接口分离(ISP) | 不依赖不需要的接口 |
| 依赖倒置(DIP) | 依赖抽象,不依赖具体实现 |
二、工厂模式
通过参数决定创建哪个对象,不用直接 new。
class User {
constructor(name) { this.name = name; }
sayHi() { console.log(`Hi, ${this.name}`); }
}
class Admin {
constructor(name) { this.name = name; }
deleteUser(id) { console.log(`${this.name} deleted user ${id}`); }
}
function createUser(type, name) {
if (type === 'admin') return new Admin(name);
return new User(name);
}
const admin = createUser('admin', 'Tom');
admin.deleteUser(1);
适用场景:根据配置或用户选择创建不同类型的对象,如弹窗、按钮、支付方式。
三、单例模式
确保一个类只有一个实例。
class Config {
static instance;
constructor() {
if (Config.instance) return Config.instance;
this.apiUrl = '/api';
Config.instance = this;
}
}
const c1 = new Config();
const c2 = new Config();
console.log(c1 === c2); // true
适用场景:全局配置、数据库连接池、全局状态管理。
四、观察者模式
一对多依赖,状态变化时自动通知所有观察者。
class Subject {
constructor() { this.observers = []; }
subscribe(fn) { this.observers.push(fn); }
notify(data) { this.observers.forEach(fn => fn(data)); }
}
const topic = new Subject();
topic.subscribe(data => console.log('订阅者1:', data));
topic.subscribe(data => console.log('订阅者2:', data));
topic.notify('新消息!');
实际应用:Vue 的响应式系统、DOM 事件监听、Redux 的 subscribe。
五、代理模式
不直接访问目标对象,通过代理控制访问。
const api = {
getUser: (id) => fetch(`/api/users/${id}`).then(r => r.json())
};
// 缓存代理
const apiProxy = new Proxy(api, {
cache: {},
get(target, prop) {
return async (id) => {
if (this.cache[prop + id]) return this.cache[prop + id];
const result = await target[prop](id);
this.cache[prop + id] = result;
return result;
};
}
});
适用场景:缓存、权限校验、日志记录、防抖节流。
六、策略模式
把可互换的算法封装成策略,运行时切换。
const strategies = {
normal: price => price,
vip: price => price * 0.8,
holiday: price => price * 0.5
};
function calculatePrice(price, type) {
return strategies[type](price);
}
console.log(calculatePrice(100, 'vip')); // 80
console.log(calculatePrice(100, 'holiday')); // 50
适用场景:多种折扣计算、表单验证规则、排序算法切换。
七、装饰器模式
不修改原对象,动态添加功能。
function withLogging(fn) {
return function(...args) {
console.log(`调用 ${fn.name},参数:`, args);
const result = fn.apply(this, args);
console.log(`返回值:`, result);
return result;
};
}
const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(1, 2);
实际应用:TypeScript 装饰器、React HOC(高阶组件)。
八、适配器模式
把不兼容的接口转换成可用的接口。
// 旧系统返回的数据格式
const oldSystem = { getData: () => ({ user_name: 'Tom', user_age: 20 }) };
// 新系统需要的格式
function displayUser({ name, age }) {
console.log(`${name}, ${age}岁`);
}
// 适配器
function adaptOldData(data) {
return { name: data.user_name, age: data.user_age };
}
displayUser(adaptOldData(oldSystem.getData()));
适用场景:对接第三方 API、迁移旧系统、兼容不同数据格式。
九、状态模式
对象状态改变时改变行为,避免大量 if/else。
class Order {
constructor() { this.state = 'pending'; }
next() {
if (this.state === 'pending') this.state = 'paid';
else if (this.state === 'paid') this.state = 'shipped';
else if (this.state === 'shipped') this.state = 'completed';
console.log('当前状态:', this.state);
}
}
适用场景:订单流转、播放器状态(播放/暂停/停止)、表单状态。
十、迭代器模式
统一遍历不同数据结构的方式。
// ES6 原生迭代器协议
const arr = [1, 2, 3];
const map = new Map([['a', 1]]);
for (const item of arr) console.log(item);
for (const [key, value] of map) console.log(key, value);
适用场景:遍历数组、Set、Map、自定义集合。
总结
| 模式 | 一句话记忆 |
|---|---|
| 工厂 | 根据参数创建对象 |
| 单例 | 全局唯一实例 |
| 观察者 | 状态变化自动通知 |
| 代理 | 控制访问,加缓存/日志 |
| 策略 | 算法可互换 |
| 装饰器 | 动态加功能不改原代码 |
| 适配器 | 接口转换 |
| 状态 | 状态驱动行为 |
| 迭代器 | 统一遍历接口 |
相关阅读
- ECMAScript文档 — ES6+ 语法速查
- JavaScript正则表达式手册 — 正则速查
- Refactoring Guru 设计模式 — 图解设计模式

浙公网安备 33010602011771号