JavaScript 设计模式与示例
设计模式 (design patterns) 是软件开发中常见问题的经过验证的解决方案。对于 JavaScript 开发者来说,了解设计模式尤其重要,因为该语言具有灵活的对象模型,并广泛用于客户端和服务器端 (Node.js)。在本文中,我们将通过 JavaScript 的实际示例来解析四种关键设计模式。
为什么要学习设计模式?
设计模式有助于:
- 简化代码的维护和扩展
- 使代码更具可读性和可预测性
- 避免"重复造轮子"
- 改善团队沟通(通用语言)
1. Singleton (单例模式)
Singleton 模式确保一个类只有一个实例,并提供一个全局访问点。在 Node.js 中,此模式常用于数据库连接、配置管理或日志记录。
class DatabaseConnection { constructor() { if (DatabaseConnection.instance) { return DatabaseConnection.instance; } this.connection = null; DatabaseConnection.instance = this; }
connect() { // 模拟数据库连接 this.connection = "connected"; console.log("数据库连接已建立"); }
query(sql) { if (!this.connection) { throw new Error("没有数据库连接"); } console.log(`执行查询: ${sql}`); return { data: [] }; }}
// 使用const db1 = new DatabaseConnection();const db2 = new DatabaseConnection();
db1.connect();console.log(db1 === db2); // true - 两者指向同一个实例db2.query("SELECT * FROM users");何时使用:管理共享资源(数据库、配置、缓存)。注意:过度使用可能导致代码高度耦合。
2. Factory (工厂模式)
Factory Method 定义创建对象的接口,但允许子类更改所创建对象的类型。在 JavaScript 中,这通常通过普通函数或类来实现。
class User { constructor(name, role) { this.name = name; this.role = role; }
getPermissions() { return []; }}
class Admin extends User { getPermissions() { return ["read", "write", "delete", "manage"]; }}
class Editor extends User { getPermissions() { return ["read", "write"]; }}
class Viewer extends User { getPermissions() { return ["read"]; }}
// 工厂class UserFactory { createUser(name, role) { switch (role) { case "admin": return new Admin(name, role); case "editor": return new Editor(name, role); case "viewer": return new Viewer(name, role); default: throw new Error("未知角色"); } }}
// 使用const factory = new UserFactory();const admin = factory.createUser("安娜", "admin");const viewer = factory.createUser("伊万", "viewer");
console.log(admin.getPermissions()); // ["read", "write", "delete", "manage"]console.log(viewer.getPermissions()); // ["read"]优点:集中管理对象创建,易于添加新类型,隐藏实例化逻辑。
3. Observer (观察者模式)
Observer 模式定义了对象之间的一对多依赖关系。当一个对象(主题)改变状态时,所有依赖对象(观察者)都会自动收到通知。这是 Node.js 中事件驱动编程的基础。
class EventEmitter { constructor() { this.events = {}; }
on(eventName, listener) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(listener); }
emit(eventName, ...args) { const listeners = this.events[eventName]; if (listeners) { listeners.forEach(listener => listener(...args)); } }
off(eventName, listenerToRemove) { const listeners = this.events[eventName]; if (listeners) { this.events[eventName] = listeners.filter( listener => listener !== listenerToRemove ); } }}
// 使用const emitter = new EventEmitter();
const onUserLogin = (username) => { console.log(`用户 ${username} 已登录`);};
const onAdminLogin = (username) => { console.log(`管理员 ${username} 已登录`);};
emitter.on("login", onUserLogin);
emitter.on("login", onAdminLogin);
emitter.emit("login", "Анна");// 输出:// 用户 Анна 已登录// 管理员 Анна 已登录
emitter.off("login", onAdminLogin);
emitter.emit("login", "Иван");// 输出:// 用户 Иван 已登录何时使用:事件处理系统、实时更新、MVC 架构。注意:大量观察者可能导致性能问题。
4. Module (模块模式)
Module 模式是 JavaScript 中最常用的模式之一。它允许将代码封装在单独的作用域中,隐藏内部实现细节,仅暴露公共接口。在现代 JavaScript 中,这通过 ES modules (import/export) 实现,但经典实现使用 IIFE (Immediately Invoked Function Expression)。
// 使用 IIFE 的经典模块模式const UserModule = (function() { // 私有变量 let users = []; let nextId = 1;
// 私有函数 function validateUser(userData) { if (!userData.name || userData.name.length < 2) { throw new Error("用户名必须至少包含2个字符"); } return true; }
// 公共 API return { addUser(userData) { validateUser(userData); const user = { id: nextId++, name: userData.name, role: userData.role || "user", createdAt: new Date() }; users.push(user); return user; },
getUsers() { return [...users]; },
findUser(id) { return users.find(user => user.id === id) || null; },
deleteUser(id) { const index = users.findIndex(user => user.id === id); if (index !== -1) { return users.splice(index, 1)[0]; } return null; },
getCount() { return users.length; } };})();
// 使用const user1 = UserModule.addUser({ name: "Анна", role: "admin" });const user2 = UserModule.addUser({ name: "Иван" });
console.log(UserModule.getUsers());// 输出: [{ id: 1, name: 'Анна', role: 'admin', createdAt: ... }, { id: 2, name: 'Иван', role: 'user', createdAt: ... }]
console.log(UserModule.getCount()); // 2
// 无法访问私有变量console.log(UserModule.users); // undefinedconsole.log(UserModule.validateUser); // undefined优点:封装、命名空间管理、避免全局作用域污染。现代替代方案:ES modules (import/export) 提供更原生的模块化支持。
总结
设计模式是每个 JavaScript 开发者工具包中的重要工具。它们不是严格的规则,而是可以根据具体任务进行调整的灵活解决方案。关键不是记住所有模式,而是理解它们解决的问题以及在项目中何时应用它们。
从 Singleton 和 Module 等简单模式开始,逐渐过渡到更复杂的模式,如 Observer 和 Factory。在实践中,您会发现许多现代框架和库(如 React、Vue、Express)已经在内部积极使用这些模式。
记住:好的代码不是使用最多模式的地方,而是使用最合适的模式来解决特定问题的地方。