深入 Node.js:五大设计模式必备指南本文探讨了 Node.js 中五种流行的设计模式,包括单例模式、工厂模式、观察
我最近研究了 Node.js
中一些流行的设计和架构模式。尽管我的主要关注点在前端开发,但在这个过程中,我发现这些模式与我熟悉的前端框架有很多相似之处。这让我意识到,自己在日常开发中已经不知不觉地应用了这些设计模式,只是以前没有特别去思考它们的具体用法。
设计模式种类繁多(真的非常多),因此在本文中,我选出了五种,准备详细介绍它们。
什么是设计模式?
设计模式是经过验证且实践证明的解决方案,用于解决开发者在日常工作中遇到的问题。这些模式有助于推广最佳实践,并为设计和开发软件架构提供结构化的方法。通过使用这些模式,软件工程师能够构建可维护、安全且稳定的系统。
由于 Node.js
的灵活性,它并不强制开发者遵循特定的设计模式,而是给予选择所需模式的自由。因此,我认为这正是它今天被广泛使用的原因之一(顺便说一下,这也要归功于 JavaScript
)。
Node.js
中的五种流行设计模式
以下是我选择的五种我喜欢的设计模式。
单例模式
单例模式主要关注只能有一个实例的类,并提供对该实例的全局访问。在 Node.js
中,模块可以被缓存并在整个应用程序中共享,从而提高资源的效率。一个常见的单例模式示例是用于连接特定第三方服务(如数据库、缓存服务、电子邮件提供商等)的模块,这在 Nest.js
框架中被广泛使用。
class Redis {
constructor() {
this.connection = null;
}
static getInstance() {
if (!Redis.instance) {
Redis.instance = new Redis();
}
return Redis.instance;
}
connect() {
this.connection = 'Redis 已连接';
}
}
使用示例:
const redisOne = Redis.getInstance();
const redisTwo = Redis.getInstance();
console.log(redisOne === redisTwo); // 结果为 `true`
redisOne.connect();
console.log(redisOne.connection); // 'Redis 已连接'
console.log(redisTwo.connection); // 'Redis 已连接'
这种方法确保只有一个 Redis
连接,从而防止连接的重复。
工厂模式
工厂模式允许你在不指定将要创建的对象类的情况下生成新对象。它抽象化了对象的创建,从而提高代码的可读性和可重用性:
class Character {
constructor(type, health) {
this.type = type;
this.health = health;
}
}
class CharacterFactory {
createCharacter(name) {
switch(name) {
case 'mage':
return new Character('强大的法师', 8);
case 'warrior':
return new Character('勇敢的战士', 10);
case 'rogue':
return new Character('狡猾的盗贼', 9);
default:
return new Error('未知角色');
}
}
}
使用示例:
const characterFactory = new CharacterFactory();
const mage = characterFactory.createCharacter('mage');
const warrior = characterFactory.createCharacter('warrior');
console.log(mage.type); // 强大的法师
console.log(warrior.type); // 勇敢的战士
这种方法使得使用这个工厂的消费者能够使用工厂代码,而不是直接使用 Character
类的构造函数。
观察者模式
观察者模式的核心在于,有一个管理依赖元素(称为观察者)列表的实体,当状态改变时,它会通知这些观察者。该模式在 Vue.js
框架中被广泛应用,可以这样实现:
class Topic {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(o => o !== observer);
}
notify(data) {
this.observers.forEach(o => o.update(data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} 收到了 ${data}`);
}
}
使用示例:
const topic = new Topic();
const observer1 = new Observer('观察者 1');
const observer2 = new Observer('观察者 2');
topic.subscribe(observer1);
topic.subscribe(observer2);
topic.notify('你好,世界');
// 观察者 1 收到 你好,世界
// 观察者 2 收到 你好,世界
topic.unsubscribe(observer2);
topic.notify('你好,再见');
// 观察者 1 收到 你好,再见
这是一个非常有用的模式,适用于事件处理和异步工作流,它允许更新多个对象,而不将发布者与订阅者耦合在一起。
装饰者模式
装饰者模式适用于在不影响初始实例的情况下,扩展现有功能。该模式在 Nest.js
对 TypeScript
的全面支持下得到了广泛应用,在普通的 Node.js
中同样适用:
class Character {
constructor() {
this.endurance = 10;
}
getEndurance() {
return this.endurance;
}
}
class CharacterActions {
constructor(character) {
this.character = character;
}
attack() {
this.character.endurance -= 2;
}
rest() {
this.character.endurance += 1;
}
}
使用示例:
const character = new Character();
console.log(character.getEndurance()); // 10
const characterWithActions = new CharacterActions(character);
characterWithActions.attack(); // - 2
characterWithActions.rest(); // + 1
console.log(characterWithActions.character.getEndurance()); // 9
通过使用这个模式,我们可以轻松扩展现有类,而不影响它们的核心功能。
依赖注入模式
在依赖注入模式中,类或模块从外部源接收依赖,而不是在内部注册这些依赖。这种方法允许将系统中的某些可重用元素提取出来,以便于测试和维护。这种模式在 Nest.js
框架中得到了广泛应用。可以通过以下方式实现:
class UserService {
constructor(databaseService, loggerService) {
this.db = databaseService;
this.logger = loggerService;
}
async getUser(userId) {
const user = await this.db.findUserById(userId);
this.logger.log(`获取用户 ${user.name}`);
return user;
}
}
使用示例:
const databaseService = new Database();
const loggerService = new Logger();
const userService = new UserService(databaseService, loggerService);
userService.getUser(1);
这种方法允许你将系统中的元素提取为独立的实体,可以在需要时进行注入。
总结
通过这五种设计模式的介绍,可以看到它们在 Node.js
开发中的重要性。这些模式不仅提高了代码的可维护性和可读性,还使得开发者能够以更结构化的方式解决问题。希望这些模式能对你的开发工作有所帮助!
转载自:https://juejin.cn/post/7425974185320546331