知识篇 -- JS设计模式:提升代码质量
Ray Shine
2024/2/6 JavaScript进阶知识设计模式
本文为博主原创文章,遵循
CC 4.0 BY-SA
版权协议,转载请附上原文出处链接和本声明。
如有侵权,请联系
本博主
删除。
设计模式是软件开发中经过实践验证的、解决常见问题的通用解决方案。它们不是可以直接使用的代码,而是解决特定问题的思想和方法论。在JavaScript中应用设计模式,可以帮助我们编写出更具结构化、可复用、可扩展和易于维护的代码。本文将介绍JavaScript中一些常用的设计模式。
# 一、创建型模式 (Creational Patterns) 对象创建
创建型模式关注对象的创建机制,旨在以一种适合特定情况的方式创建对象。
# 1. 单例模式 (Singleton Pattern) 唯一实例
- 定义:确保一个类只有一个实例,并提供一个全局访问点。
- 应用场景:管理配置、日志记录器、全局状态管理(如Redux的Store)。
- 示例:
class SingletonLogger { constructor() { if (!SingletonLogger.instance) { this.logs = []; SingletonLogger.instance = this; } return SingletonLogger.instance; } log(message) { this.logs.push(message); console.log(`LOG: ${message}`); } getLogs() { return this.logs; } } const logger1 = new SingletonLogger(); const logger2 = new SingletonLogger(); console.log(logger1 === logger2); // true (两者是同一个实例) logger1.log("User logged in."); logger2.log("Data fetched."); console.log(logger1.getLogs()); // ["User logged in.", "Data fetched."]
# 2. 工厂模式 (Factory Pattern) 按需创建
- 定义:定义一个创建对象的接口,但让子类决定实例化哪个类。工厂方法将类的实例化延迟到子类。
- 应用场景:根据不同条件创建不同类型的对象,而无需暴露创建逻辑。
- 示例:
class Car { constructor(options) { this.doors = options.doors || 4; this.color = options.color || "black"; } } class Truck { constructor(options) { this.wheels = options.wheels || 6; this.payload = options.payload || "1000kg"; } } class VehicleFactory { createVehicle(type, options) { switch (type) { case "car": return new Car(options); case "truck": return new Truck(options); default: return null; } } } const factory = new VehicleFactory(); const myCar = factory.createVehicle("car", { color: "red" }); const myTruck = factory.createVehicle("truck", { wheels: 8 }); console.log(myCar); // Car { doors: 4, color: "red" } console.log(myTruck); // Truck { wheels: 8, payload: "1000kg" }
# 二、结构型模式 (Structural Patterns) 结构组合
结构型模式关注如何组合对象和类以形成更大的结构。
# 1. 适配器模式 (Adapter Pattern) 接口转换
- 定义:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 应用场景:整合旧API与新系统、统一不同第三方库的接口。
- 示例:
// 旧API class OldCalculator { add(num1, num2) { return num1 + num2; } } // 新接口期望 class NewCalculator { operate(operator, num1, num2) { if (operator === "+") { return num1 + num2; } return NaN; } } // 适配器 class CalculatorAdapter { constructor() { this.oldCalculator = new OldCalculator(); } operate(operator, num1, num2) { if (operator === "+") { return this.oldCalculator.add(num1, num2); } return NaN; } } const newCalc = new NewCalculator(); console.log(newCalc.operate("+", 10, 5)); // 15 const adapter = new CalculatorAdapter(); console.log(adapter.operate("+", 10, 5)); // 15 (通过适配器调用旧API)
# 2. 装饰器模式 (Decorator Pattern) 动态增强
- 定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
- 应用场景:日志记录、权限检查、数据校验、React高阶组件 (HOC)。
- 示例:
// 原始对象 class Coffee { cost() { return 5; } description() { return "Plain Coffee"; } } // 装饰器:加牛奶 class MilkDecorator { constructor(coffee) { this.coffee = coffee; } cost() { return this.coffee.cost() + 2; } description() { return this.coffee.description() + ", Milk"; } } // 装饰器:加糖 class SugarDecorator { constructor(coffee) { this.coffee = coffee; } cost() { return this.coffee.cost() + 1; } description() { return this.coffee.description() + ", Sugar"; } } let myCoffee = new Coffee(); myCoffee = new MilkDecorator(myCoffee); // 加牛奶 myCoffee = new SugarDecorator(myCoffee); // 再加糖 console.log(myCoffee.description()); // Plain Coffee, Milk, Sugar console.log(myCoffee.cost()); // 8
# 三、行为型模式 (Behavioral Patterns) 行为交互
行为型模式关注对象之间的通信和职责分配。
# 1. 观察者模式 (Observer Pattern) / 发布-订阅模式 (Publish-Subscribe Pattern) 事件通知
- 定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 应用场景:事件系统、消息通知、数据绑定、Vue/React的状态管理。
- 示例:
class Subject { constructor() { this.observers = []; } addObserver(observer) { this.observers.push(observer); } removeObserver(observer) { this.observers = this.observers.filter(obs => obs !== observer); } notify(data) { this.observers.forEach(observer => observer.update(data)); } } class Observer { constructor(name) { this.name = name; } update(data) { console.log(`${this.name} received update: ${data}`); } } const subject = new Subject(); const obs1 = new Observer("Observer 1"); const obs2 = new Observer("Observer 2"); subject.addObserver(obs1); subject.addObserver(obs2); subject.notify("New event occurred!"); // Observer 1 received update: New event occurred! // Observer 2 received update: New event occurred! subject.removeObserver(obs1); subject.notify("Another event!"); // Observer 2 received update: Another event!
# 2. 策略模式 (Strategy Pattern) 算法封装
- 定义:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。
- 应用场景:表单验证、支付方式选择、不同计算规则。
- 示例:
const validator = { types: {}, messages: [], config: {}, validate: function(data) { this.messages = []; for (let i in data) { if (this.config[i]) { const type = this.config[i].type; const strategy = this.types[type]; if (!strategy.validate(data[i], this.config[i].value)) { this.messages.push(strategy.instructions); } } } return this.hasErrors(); }, hasErrors: function() { return this.messages.length !== 0; } }; validator.types.isNonEmpty = { validate: function(value) { return value !== ""; }, instructions: "值不能为空。" }; validator.types.isNumber = { validate: function(value) { return !isNaN(value); }, instructions: "值必须是数字。" }; validator.config = { username: { type: "isNonEmpty" }, age: { type: "isNumber" } }; const data1 = { username: "John", age: 25 }; const data2 = { username: "", age: "abc" }; validator.validate(data1); console.log("Data1 errors:", validator.messages); // [] validator.validate(data2); console.log("Data2 errors:", validator.messages); // ["值不能为空。", "值必须是数字。"]
# 四、总结
设计模式是软件工程的宝贵财富,它们提供了一种通用的语言来讨论和解决软件设计中的问题。在JavaScript开发中,合理地运用设计模式,不仅可以提升代码的质量和可维护性,还能帮助开发者更好地理解和应对复杂的业务需求。