当谈到编写干净、可维护和高效的代码时,设计模式在软件开发领域发挥着至关重要的作用。设计模式是开发人员在设计和构建软件系统时面临的常见问题的可重用解决方案。它们提供了一种结构化的方法来解决特定的挑战,使创建不仅健壮而且更易于理解和维护的代码变得更加容易。
在面向对象编程 (OOP ) 中,设计模式充当以提高灵活性、可重用性和可扩展性的方式构建代码的指南。它们概括了已经发展的最佳实践和设计原则,并提炼成经过验证的解决方案。
设计模式可以分为三大类:
创建模式:这些模式侧重于对象创建机制,尝试以适合情况的方式创建对象。它们抽象了实例化过程,使其更加灵活并且独立于系统。
结构模式:结构模式处理对象组合,形成对象之间的关系以创建更大、更复杂的结构。它们有助于定义如何组合对象和类以形成新结构并提供新功能。
行为模式:行为模式涉及对象之间的通信,定义它们如何交互和分配责任。这些模式可帮助您设计对象以更灵活、更高效的方式协作的系统。
以下是每个类别中一些常见设计模式的列表:
观察者模式:定义对象之间的一对多依赖关系,因此当一个对象更改状态时,它的所有依赖项都会收到通知并自动更新。
策略模式:定义一系列算法,封装每个算法,并使它们可以互换。
命令模式:将请求封装为对象,从而允许使用队列、请求和操作对客户端进行参数化。
状态模式:允许对象在其内部状态发生变化时改变其行为,将行为包装在单独的类中。
责任链模式:沿着处理程序链传递请求,允许每个处理程序决定是处理请求还是将其传递给链中的下一个处理程序。
访问者模式:这表示要对对象结构的元素执行的操作,使您能够定义新操作而不更改元素的类。
在本博客中,我们将深入研究每种设计模式,提供解释、实际用例和JavaScript代码示例,以帮助您在项目中有效地理解和实现它们。
单例模式是一种创建型设计模式,可确保类只有一个实例并提供对该实例的全局访问点。当您想要限制应用程序中类的实例数量并控制对单个共享实例的访问时,此模式特别有用。
在 JavaScript 中,由于语言的灵活性,实现单例模式相对简单。让我们深入研究如何在 JavaScript 中创建单例的简单示例。
// Singleton instance let instance = null;
class Singleton { constructor() { if (!instance) { instance = this; // Your initialization code here } else { return instance; } } // Your methods and properties here }// Usage const singletonA = new Singleton(); const singletonB = new Singleton(); console.log(singletonA === singletonB); // Output: true (both variables reference the same instance)
在此示例中,我们创建一个带有构造函数的 Singleton 类,该构造函数检查实例是否已存在。如果实例不存在,它将创建一个实例并将其分配给实例变量。对构造函数的后续调用将返回现有实例,确保 Singleton 类只有一个实例。
单例模式在各种场景中都很有用,包括:
虽然单例模式可能很有用,但明智地使用它也很重要。过度使用单例模式可能会导致代码和全局状态紧密耦合,这可能会使您的应用程序更难以维护和测试。因此,权衡利弊并在真正为代码库增加价值的地方应用该模式至关重要。
工厂模式和抽象工厂模式是处理对象创建的创建型设计模式,但它们以不同的方式实现并服务于不同的目的。让我们探索每一个模式,看看如何在 JavaScript 中实现它们。
工厂模式是一种创建模式,它提供用于创建对象的接口,但允许子类更改将创建的对象的类型。它封装了对象创建过程,使其更加灵活并与客户端代码解耦。
// Product class class Product { constructor(name) { this.name = name; } }
// Factory for creating products class ProductFactory { createProduct(name) { return new Product(name); } }// Usage const factory = new ProductFactory(); const productA = factory.createProduct('Product A'); const productB = factory.createProduct('Product B');console.log(productA.name); // Output: 'Product A' console.log(productB.name); // Output: 'Product B'
在此示例中,ProductFactory 负责创建 Product 类的实例。它抽象了创建过程,允许您通过扩展工厂来创建不同类型的产品。
抽象工厂模式是另一种创建模式,它提供了一个接口,用于创建相关或依赖对象系列,而无需指定它们的具体类。它允许您创建和谐协作的对象集。
// Abstract Product classes class Button { render() {} }
class Checkbox { render() {} }// Concrete Product classes class MacButton extends Button { render() { return 'Render Mac button'; } }class MacCheckbox extends Checkbox { render() { return 'Render Mac checkbox'; } }class WindowsButton extends Button { render() { return 'Render Windows button'; } }class WindowsCheckbox extends Checkbox { render() { return 'Render Windows checkbox'; } }// Abstract Factory interface class GUIFactory { createButton() {} createCheckbox() {} }// Concrete Factories class MacFactory extends GUIFactory { createButton() { return new MacButton(); } createCheckbox() { return new MacCheckbox(); } }class WindowsFactory extends GUIFactory { createButton() { return new WindowsButton(); } createCheckbox() { return new WindowsCheckbox(); } }// Usage function createUI(factory) { const button = factory.createButton(); const checkbox = factory.createCheckbox(); return { button, checkbox }; }const macUI = createUI(new MacFactory()); console.log(macUI.button.render()); // Output: 'Render Mac button' console.log(macUI.checkbox.render()); // Output: 'Render Mac checkbox'const windowsUI = createUI(new WindowsFactory()); console.log(windowsUI.button.render()); // Output: 'Render Windows button' console.log(windowsUI.checkbox.render()); // Output: 'Render Windows checkbox'
在此示例中,我们有两个具体工厂:MacFactory 和 WindowsFactory,每个工厂都能够为其各自的平台创建一组相关的 UI 组件(按钮和复选框)。 createUI 函数允许您使用适当的工厂为特定平台创建内聚的 UI。
何时使用哪种模式:
构建器模式是一种创建型设计模式,它将复杂对象的构造与其表示分离,允许相同的构造过程创建不同的表示。当您的对象具有大量属性,并且您希望在保持灵活性的同时简化实例的创建时,此模式特别有用。
在 JavaScript 中,构建器模式通常使用构建器类或对象来实现,该构建器类或对象指导复杂对象的逐步构建。让我们深入研究一个示例来了解它是如何工作的。
// Product class with multiple properties class Product { constructor() { this.name = ''; this.price = 0; this.color = 'white'; // ... other properties }
// Additional methods can be defined here }// Builder for creating Product instances class ProductBuilder { constructor() { this.product = new Product(); } setName(name) { this.product.name = name; return this; // Return the builder for method chaining } setPrice(price) { this.product.price = price; return this; } setColor(color) { this.product.color = color; return this; } // Other methods to set additional properties build() { return this.product; // Return the fully constructed product } }// Usage const builder = new ProductBuilder();const productA = builder .setName('Product A') .setPrice(99.99) .setColor('blue') .build();const productB = builder .setName('Product B') .setPrice(49.99) .build();console.log(productA); console.log(productB);
在此示例中,我们有一个具有多个属性的 Product 类。 ProductBuilder 类通过提供逐步设置每个属性的方法来帮助创建 Product 实例。方法链接允许您以流畅且可读的方式设置多个属性。最后,build 方法返回完全构建的 Product 实例。
构建器模式在各种场景中都有用,包括:
虽然构建器模式提供了许多优点,但值得注意的是,它增加了代码库的复杂性,特别是在构建的对象相对简单的情况下。因此,有必要评估 Builder 引入的复杂性是否适合您的特定用例。
原型模式是一种创建型设计模式,允许您通过复制现有对象(称为原型)来创建新对象。它促进对象的创建,而无需指定要创建的确切对象类。当您想要高效地创建复杂对象的实例时,此模式特别有用。
在 JavaScript 中,原型模式与内置的prototype
属性和Object.create()
方法密切相关。让我们探讨如何在 JavaScript 中实现和使用原型模式。
// Prototype object const vehiclePrototype = { init(make, model) { this.make = make; this.model = model; }, getDetails() { return `${this.make} ${this.model}`; }, };
// Create new instances using the prototype const car1 = Object.create(vehiclePrototype); car1.init('Toyota', 'Camry');const car2 = Object.create(vehiclePrototype); car2.init('Honda', 'Civic');console.log(car1.getDetails()); // Output: 'Toyota Camry' console.log(car2.getDetails()); // Output: 'Honda Civic'
在此示例中,我们定义了一个vehiclePrototype
对象,其中包含所有车辆通用的方法和属性。我们使用Object.create()
基于此原型创建新实例(car1 和 car2)。这些实例继承了原型的属性和方法,允许您有效地创建具有共享行为的新对象。
原型模式在各种场景中都很有价值,包括:
虽然原型模式很有用,但它有一些注意事项:
对象池模式是一种创建型设计模式,它管理可重用对象池,以最大限度地减少对象创建和销毁的开销。当创建和销毁对象成本昂贵或资源密集型时,它特别有用。对象池模式通过回收和重用对象而不是从头开始创建新对象来帮助提高性能和资源利用率。
在 JavaScript 中,您可以使用数组或自定义池管理类来实现对象池模式。让我们通过一个简单的示例来探讨该模式的工作原理。
class ObjectPool { constructor(maxSize) { this.maxSize = maxSize; this.pool = []; }
create() { if (this.pool.length < this.maxSize) { // Create a new object and add it to the pool const obj = { /* Your object initialization code here */ }; this.pool.push(obj); return obj; } else { // Pool is full, cannot create more objects console.log('Pool is full. Cannot create more objects.'); return null; } } reuse() { if (this.pool.length > 0) { // Reuse an object from the pool return this.pool.pop(); } else { // Pool is empty, no objects available for reuse console.log('Pool is empty. No objects available for reuse.'); return null; } } release(obj) { // Release an object back to the pool for reuse this.pool.push(obj); } }// Usage const pool = new ObjectPool(5); // Create a pool with a maximum size of 5 objectsconst obj1 = pool.create(); const obj2 = pool.create(); const obj3 = pool.create();pool.release(obj2); // Release obj2 back to the pool for reuseconst obj4 = pool.reuse(); // Reuse an object from the pool (obj2)
在此示例中,我们创建一个管理对象池的 ObjectPool 类。 create 方法在池未满时创建新对象,reuse 方法从池中检索对象以供重用,release 方法将对象返回到池中以供将来使用。
对象池模式在各种场景中都很有用,包括:
虽然对象池模式提供了性能优势,但考虑以下事项也很重要:
适配器模式是一种结构设计模式,允许具有不兼容接口的对象一起工作。它充当两个不兼容接口之间的桥梁,使它们在不更改源代码的情况下兼容。当您需要集成或使用不太符合应用程序要求的现有代码时,此模式特别有用。
在 JavaScript 中,适配器模式可以使用包装或适应不兼容接口的类或函数来实现。让我们通过一个实际示例来探讨如何在 JavaScript 中实现和使用适配器模式。
假设您有一个名为OldSystem
的现有类,其中包含一个名为legacyRequest
的方法:
class OldSystem { legacyRequest() { return 'Data from the legacy system'; } }
现在,您希望在需要不同界面的现代应用程序中使用这个遗留系统。您可以创建一个适配器类或函数,如下所示:
class Adapter { constructor(oldSystem) { this.oldSystem = oldSystem; }
newRequest() { const legacyData = this.oldSystem.legacyRequest(); // Adapt the data or perform any necessary transformations return `Adapted: ${legacyData}`; } }
现在,您可以使用 Adapter 类使遗留系统与您的现代应用程序兼容:
const oldSystem = new OldSystem(); const adapter = new Adapter(oldSystem);
const result = adapter.newRequest(); console.log(result); // Output: 'Adapted: Data from the legacy system'
在此示例中,Adapter 类包装了 OldSystem 并提供了一个新接口 newRequest,它与您的现代应用程序兼容。
适配器模式在各种场景中都很有价值,包括:
虽然适配器模式提供了灵活性和兼容性,但必须考虑以下几点:
装饰器模式是一种结构设计模式,允许您动态地向对象添加新的行为或职责,而无需更改其现有代码。这是通过用装饰器对象包装对象来扩展对象功能的有效方法。这种模式提倡“开放扩展,但封闭修改”的原则,使得可以轻松地向对象添加新功能,而无需更改其核心实现。
在 JavaScript 中,装饰器模式可以使用类和对象组合来实现。让我们通过一个实际示例来探讨如何在 JavaScript 中实现和使用装饰器模式。
假设您有一个基类Coffee
:
class Coffee { cost() { return 5; // Base cost of a regular coffee } } Now, you want to add decorators to your coffee to customize it with additional options, such as milk and sugar:
javascript Copy code class MilkDecorator { constructor(coffee) { this.coffee = coffee; } cost() { return this.coffee.cost() + 2; // Adding the cost of milk } }class SugarDecorator { constructor(coffee) { this.coffee = coffee; } cost() { return this.coffee.cost() + 1; // Adding the cost of sugar } }
然后,您可以像这样创建装饰咖啡实例:
const regularCoffee = new Coffee(); const coffeeWithMilk = new MilkDecorator(regularCoffee); const coffeeWithMilkAndSugar = new SugarDecorator(coffeeWithMilk);
console.log(regularCoffee.cost()); // Output: 5 console.log(coffeeWithMilk.cost()); // Output: 7 console.log(coffeeWithMilkAndSugar.cost()); // Output: 8
在此示例中,我们有代表基础咖啡的 Coffee 类。 MilkDecorator 和 SugarDecorator 类是包装咖啡对象并将牛奶和糖的成本分别添加到基本成本的装饰器。
装饰器模式在各种场景中都很有价值,包括:
虽然装饰器模式用途广泛,但记住一些注意事项很重要:
代理模式是一种结构设计模式,它为另一个对象提供代理或占位符来控制对其的访问。它充当目标对象的中介或包装器,允许您添加其他行为、控制访问或延迟对象创建。代理模式在各种场景中都很有用,例如实现延迟加载、访问控制和日志记录。
在 JavaScript 中,可以使用内置的Proxy
对象创建代理。让我们通过实际例子来探讨如何在 JavaScript 中实现和使用代理模式。
假设您有一个资源密集型对象,您只想在需要时才延迟加载。可以使用代理来实现延迟加载:
class ExpensiveResource { constructor() { console.log('Creating an expensive resource...'); }
fetchData() { console.log('Fetching data...'); } }class LazyResourceProxy { constructor() { this.resource = null; } fetchData() { if (!this.resource) { this.resource = new ExpensiveResource(); } this.resource.fetchData(); } }// Usage const lazyResource = new LazyResourceProxy(); // The actual resource is created and data is fetched only when needed lazyResource.fetchData();
在此示例中,LazyResourceProxy 充当 ExpenseResource 的代理,仅在第一次调用 fetchData 方法时创建实际资源。
您还可以使用代理来控制对对象及其属性的访问:
const user = { username: 'john_doe', password: 'secret123', };
const userProxy = new Proxy(user, { get(target, property) { if (property === 'password') { throw new Error('Access denied to password.'); } return target[property]; }, });console.log(userProxy.username); // Output: 'john_doe' console.log(userProxy.password); // Throws an error: 'Access denied to password.'
在此示例中,代理拦截 get 操作并限制对密码属性的访问。
代理模式在各种场景中都很有价值,包括:
使用代理模式时,请记住以下注意事项:
复合模式是一种结构设计模式,允许您将对象组合成树状结构来表示部分-整体层次结构。它允许客户统一处理单个对象和对象组合。当您需要处理由较小的相关对象组成的复杂结构,同时保持一致的界面时,复合模式特别有用。
在 JavaScript 中,您可以使用共享公共接口的类或对象来实现复合模式,从而使您能够构建层次结构。让我们通过实际示例探讨如何在 JavaScript 中实现和使用复合模式。
假设您正在构建一个图形设计应用程序,该应用程序需要处理简单的形状和复杂的形状组合(例如,组)。您可以使用复合模式来表示此层次结构:
// Component interface class Graphic { draw() {} }
// Leaf class (represents simple shapes) class Circle extends Graphic { constructor() { super(); // Circle-specific properties and methods } draw() { // Draw a circle } }// Composite class (represents groups of shapes) class Group extends Graphic { constructor() { super(); this.graphics = []; } add(graphic) { this.graphics.push(graphic); } draw() { // Draw each graphic in the group this.graphics.forEach((graphic) => graphic.draw()); } }// Usage const circle1 = new Circle(); const circle2 = new Circle(); const group = new Group();group.add(circle1); group.add(circle2);group.draw(); // Draws both circles in the group
在此示例中,Graphic 类充当组件接口。 Circle 类表示简单形状,而 Group 类表示形状的组合。 Circle 和 Group 类都实现了 draw 方法,允许您在渲染时统一对待它们。
复合模式在各种场景中都很有价值,包括:
使用复合模式时,请考虑以下事项:
桥接模式是一种结构设计模式,它将对象的抽象与其实现分开。它允许您在两者之间建立一座桥梁,使它们能够独立变化。当您想要避免抽象与其实现之间的永久绑定时,此模式特别有用,从而使您的代码更加灵活和可维护。
在 JavaScript 中,桥接模式可以使用类和对象来实现,这些类和对象为抽象提供抽象接口,并为各种平台或功能提供不同的具体实现。让我们通过实际示例探讨如何在 JavaScript 中实现和使用桥接模式。
假设您正在构建一个绘图应用程序,可以在不同平台(例如 Web 浏览器和移动设备)上渲染形状。您可以使用桥接模式将绘图形状(抽象)与渲染逻辑(实现)分开:
// Abstraction class Shape { constructor(renderer) { this.renderer = renderer; }
draw() { // Delegating the drawing to the specific renderer this.renderer.renderShape(this); } }// Implementor interface class Renderer { renderShape(shape) {} }// Concrete Implementors class WebRenderer extends Renderer { renderShape(shape) { console.log(`Drawing on the web: ${shape.constructor.name}`); } }class MobileRenderer extends Renderer { renderShape(shape) { console.log(`Drawing on mobile: ${shape.constructor.name}`); } }// Concrete Abstractions (Shapes) class Circle extends Shape { constructor(renderer) { super(renderer); } }class Square extends Shape { constructor(renderer) { super(renderer); } }// Usage const webRenderer = new WebRenderer(); const mobileRenderer = new MobileRenderer();const circle = new Circle(webRenderer); const square = new Square(mobileRenderer);circle.draw(); // Output: Drawing on the web: Circle square.draw(); // Output: Drawing on mobile: Square
在此示例中,Shape 类表示抽象(要绘制的形状),Renderer 类表示实现者接口(特定于平台的渲染逻辑)。不同的具体实现器(WebRenderer 和 MobileRenderer)分别为 Web 和移动平台提供渲染逻辑。 Circle 和 Square 类是表示形状的具体抽象。
桥接模式在各种场景中都很有价值,包括:
使用桥接模式时,请考虑以下事项:
享元模式是一种结构设计模式,旨在通过共享对象的公共部分来减少内存消耗并提高性能。它通过将对象的内在状态(共享且不可变)与其外在状态(唯一且依赖于上下文)分离来实现此目的。当您有大量相似的对象并希望最大限度地减少内存占用时,此模式特别有用。
在 JavaScript 中,您可以使用类或对象来表示共享的内部状态和单独的外部状态来实现享元模式。让我们通过实际示例来探讨如何在 JavaScript 中实现和使用享元模式。
假设您正在开发一个需要显示大量文本的文本编辑器。当角色对象具有相同的内在属性(例如,字体和大小)时,您可以使用享元模式来共享角色对象,而不是为每个角色创建单独的对象:
class Character { constructor(char, font, size) { this.char = char; this.font = font; this.size = size; }
render() { console.log(`Rendering character "${this.char}" in ${this.font}, size ${this.size}`); } }class CharacterFactory { constructor() { this.characters = {}; } getCharacter(char, font, size) { const key = `${char}-${font}-${size}`; if (!this.characters[key]) { this.characters[key] = new Character(char, font, size); } return this.characters[key]; } }// Usage const factory = new CharacterFactory();const charA1 = factory.getCharacter('A', 'Arial', 12); const charA2 = factory.getCharacter('A', 'Arial', 12); const charB = factory.getCharacter('B', 'Times New Roman', 14);charA1.render(); // Output: Rendering character "A" in Arial, size 12 charA2.render(); // Output: Rendering character "A" in Arial, size 12 (shared instance) charB.render(); // Output: Rendering character "B" in Times New Roman, size 14
在此示例中,Character 类表示具有字符本身、字体和大小等内在属性的单个字符。 CharacterFactory 类确保具有相同内在属性的字符被共享而不是重复。
享元模式在各种场景中都很有价值,包括:
使用享元模式时,请考虑以下事项:
观察者模式是一种行为设计模式,它在对象之间建立一对多的依赖关系。它允许一个对象(主体或可观察对象)向多个观察者(侦听器)通知其状态或数据的变化。此模式通常用于实现分布式事件处理系统,其中一个对象的状态更改会触发其他依赖对象中的操作。
在 JavaScript 中,您可以使用自定义类或内置功能(例如事件侦听器和addEventListener
方法)来实现观察者模式。让我们通过实际例子来探讨如何在 JavaScript 中实现和使用观察者模式。
假设您正在构建一个天气应用程序,并且您希望 UI 的不同部分在天气条件发生变化时更新。您可以使用观察者模式的自定义实现:
class WeatherStation { constructor() { this.observers = []; }
addObserver(observer) { this.observers.push(observer); } removeObserver(observer) { const index = this.observers.indexOf(observer); if (index !== -1) { this.observers.splice(index, 1); } } notifyObservers() { this.observers.forEach((observer) => { observer.update(this); }); } setWeatherData(weatherData) { this.weatherData = weatherData; this.notifyObservers(); } }class WeatherDisplay { update(weatherStation) { console.log(`Current weather: ${weatherStation.weatherData}`); } }// Usage const weatherStation = new WeatherStation(); const display1 = new WeatherDisplay(); const display2 = new WeatherDisplay();weatherStation.addObserver(display1); weatherStation.addObserver(display2);weatherStation.setWeatherData('Sunny'); // Both displays update with the new weather data
在此示例中,WeatherStation 充当主体,在天气数据发生变化时通知观察者(显示对象)。观察者使用 addObserver 方法订阅主题,并实现 update 方法来对更改做出反应。
JavaScript 还提供了一种使用事件监听器实现观察者模式的内置方法:
class NewsPublisher { constructor() { this.subscribers = []; }
subscribe(subscriber) { this.subscribers.push(subscriber); } unsubscribe(subscriber) { const index = this.subscribers.indexOf(subscriber); if (index !== -1) { this.subscribers.splice(index, 1); } } publishNews(news) { this.subscribers.forEach((subscriber) => { subscriber(news); }); } }// Usage const publisher = new NewsPublisher();const subscriber1 = (news) => { console.log(`Subscriber 1 received news: ${news}`); };const subscriber2 = (news) => { console.log(`Subscriber 2 received news: ${news}`); };publisher.subscribe(subscriber1); publisher.subscribe(subscriber2);publisher.publishNews('Breaking News: Important Announcement');
在此示例中,NewsPublisher 充当主题,并使用 subscribe 方法添加订阅者(函数)。 publishNews 方法通过调用新闻函数来通知订阅者。
观察者模式在各种场景中都很有价值,包括:
使用观察者模式时,请考虑以下事项:
策略模式是一种行为设计模式,它允许您定义一系列可互换的算法,封装每个算法,并使它们可以互换。它使客户端能够在运行时动态选择适当的算法。这种模式通过将算法的行为与使用它的上下文分开来提高灵活性和可重用性。
在 JavaScript 中,您可以使用对象或函数来表示不同的策略以及可以在这些策略之间切换的上下文对象来实现策略模式。让我们通过实际示例探讨如何在 JavaScript 中实现和使用策略模式。
假设您正在开发一个电子商务应用程序,并且您想要计算不同类型客户的折扣。您可以使用策略模式来封装折扣策略:
// Discount Strategies const regularCustomerDiscount = (amount) => amount * 0.1; // 10% discount const premiumCustomerDiscount = (amount) => amount * 0.2; // 20% discount
// Context class ShoppingCart { constructor(discountStrategy) { this.items = []; this.discountStrategy = discountStrategy; } addItem(item) { this.items.push(item); } calculateTotal() { const subtotal = this.items.reduce((total, item) => total + item.price, 0); return subtotal - this.discountStrategy(subtotal); } }// Usage const regularCustomerCart = new ShoppingCart(regularCustomerDiscount); const premiumCustomerCart = new ShoppingCart(premiumCustomerDiscount);regularCustomerCart.addItem({ name: 'Item 1', price: 50 }); premiumCustomerCart.addItem({ name: 'Item 2', price: 100 });console.log(`Regular Customer Total: $${regularCustomerCart.calculateTotal()}`); // Output: $45 (after 10% discount) console.log(`Premium Customer Total: $${premiumCustomerCart.calculateTotal()}`); // Output: $80 (after 20% discount)
在此示例中,我们将两个折扣策略定义为函数(regularCustomerDiscount 和 premiumCustomerDiscount)。 ShoppingCart 类将折扣策略作为参数,并根据所选策略计算总价。
策略模式在各种场景中都很有价值,包括:
使用策略模式时,请考虑以下事项:
命令模式是一种行为设计模式,它将请求或简单操作转换为独立的对象。它允许您使用不同的请求参数化对象,延迟或排队请求的执行,并支持可撤消的操作。这种模式将请求的发送者与其接收者解耦,从而可以轻松扩展和维护代码。
在 JavaScript 中,您可以使用对象或类来表示命令和执行这些命令的调用程序来实现命令模式。让我们通过实际示例探讨如何在 JavaScript 中实现和使用命令模式。
假设您正在为智能家居开发远程控制应用程序,并且您希望创建一种灵活的方式来控制各种设备。
您可以使用命令模式:
// Command interface class Command { execute() {} }
// Concrete Commands class LightOnCommand extends Command { constructor(light) { super(); this.light = light; } execute() { this.light.turnOn(); } }class LightOffCommand extends Command { constructor(light) { super(); this.light = light; } execute() { this.light.turnOff(); } }// Receiver (Device) class Light { turnOn() { console.log('Light is on.'); } turnOff() { console.log('Light is off.'); } }// Invoker (Remote Control) class RemoteControl { constructor() { this.commands = []; } addCommand(command) { this.commands.push(command); } executeCommands() { this.commands.forEach((command) => { command.execute(); }); } }// Usage const livingRoomLight = new Light(); const kitchenLight = new Light();const livingRoomLightOn = new LightOnCommand(livingRoomLight); const livingRoomLightOff = new LightOffCommand(livingRoomLight); const kitchenLightOn = new LightOnCommand(kitchenLight); const kitchenLightOff = new LightOffCommand(kitchenLight);const remoteControl = new RemoteControl();remoteControl.addCommand(livingRoomLightOn); remoteControl.addCommand(kitchenLightOff);remoteControl.executeCommands(); // Output: "Light is on." (for living room) // Output: "Light is off." (for kitchen)
在此示例中,命令模式用于封装开灯和关灯的操作。 RemoteControl充当调用者,具体命令(例如,LightOnCommand和LightOffCommand)封装要执行的动作。
命令模式在各种场景中都很有价值,包括:
使用命令模式时,请考虑以下事项:
状态模式是一种行为设计模式,允许对象在其内部状态发生变化时改变其行为。它将状态封装为单独的类并将行为委托给当前状态对象。这种模式有助于管理复杂的状态转换并促进“开放-封闭”原则,使得无需修改现有代码即可轻松添加新状态。
在 JavaScript 中,您可以使用表示状态的类和将其行为委托给当前状态的上下文对象来实现状态模式。让我们通过实际示例探讨如何在 JavaScript 中实现和使用状态模式。
假设您正在开发一台分配不同产品的自动售货机。自动售货机的行为取决于其当前状态,例如“就绪”、“正在分发”或“售完”。您可以使用状态模式来模拟此行为:
// State interface class VendingMachineState { insertMoney() {} ejectMoney() {} selectProduct() {} dispenseProduct() {} }
// Concrete States class ReadyState extends VendingMachineState { constructor(machine) { super(); this.machine = machine; } insertMoney() { console.log('Money inserted.'); this.machine.setState(this.machine.getDispensingState()); } selectProduct() { console.log('Please insert money first.'); } }class DispensingState extends VendingMachineState { constructor(machine) { super(); this.machine = machine; } dispenseProduct() { console.log('Product dispensed.'); this.machine.setState(this.machine.getReadyState()); } }class VendingMachine { constructor() { this.readyState = new ReadyState(this); this.dispensingState = new DispensingState(this); this.currentState = this.readyState; } setState(state) { this.currentState = state; } getReadyState() { return this.readyState; } getDispensingState() { return this.dispensingState; } insertMoney() { this.currentState.insertMoney(); } selectProduct() { this.currentState.selectProduct(); } dispenseProduct() { this.currentState.dispenseProduct(); } }// Usage const vendingMachine = new VendingMachine();vendingMachine.selectProduct(); // Output: "Please insert money first." vendingMachine.insertMoney(); // Output: "Money inserted." vendingMachine.dispenseProduct(); // Output: "Product dispensed."
在此示例中,状态模式用于管理自动售货机的行为。 “就绪”和“分配”等状态表示为单独的类,上下文(自动售货机)将其行为委托给当前状态。
状态模式在各种场景中都很有价值,包括:
使用状态模式时,请考虑以下事项:
责任链模式是一种行为设计模式,可帮助您构建对象链来处理请求。链中的每个对象都有机会处理请求或将其传递给链中的下一个对象。它将请求的发送者与其接收者解耦,并允许链中存在多个处理程序。此模式使您能够在不影响客户端代码的情况下添加或修改处理程序,从而提高了灵活性和可扩展性。
在 JavaScript 中,您可以使用代表处理程序和发起请求的客户端的对象或类来实现责任链模式。每个处理程序都有对链中下一个处理程序的引用。让我们通过实际示例探讨如何在 JavaScript 中实现和使用责任链模式。
假设您正在开发一个订单处理系统,并且您希望根据订单总额来处理订单。您可以使用责任链模式创建一个处理程序链,每个处理程序负责处理特定价格范围内的订单:
// Handler interface class OrderHandler { constructor() { this.nextHandler = null; }
setNextHandler(handler) { this.nextHandler = handler; } handleOrder(order) { if (this.canHandleOrder(order)) { this.processOrder(order); } else if (this.nextHandler) { this.nextHandler.handleOrder(order); } else { console.log('No handler can process this order.'); } } canHandleOrder(order) {} processOrder(order) {} }// Concrete Handlers class SmallOrderHandler extends OrderHandler { canHandleOrder(order) { return order.amount <= 100; } processOrder(order) { console.log(`Processing small order for ${order.amount}`); } }class MediumOrderHandler extends OrderHandler { canHandleOrder(order) { return order.amount <= 500; } processOrder(order) { console.log(`Processing medium order for ${order.amount}`); } }class LargeOrderHandler extends OrderHandler { canHandleOrder(order) { return order.amount > 500; } processOrder(order) { console.log(`Processing large order for ${order.amount}`); } }// Client class Order { constructor(amount) { this.amount = amount; } }// Usage const smallOrderHandler = new SmallOrderHandler(); const mediumOrderHandler = new MediumOrderHandler(); const largeOrderHandler = new LargeOrderHandler();smallOrderHandler.setNextHandler(mediumOrderHandler); mediumOrderHandler.setNextHandler(largeOrderHandler);const order1 = new Order(80); const order2 = new Order(250); const order3 = new Order(600);smallOrderHandler.handleOrder(order1); // Output: "Processing small order for 80" smallOrderHandler.handleOrder(order2); // Output: "Processing medium order for 250" smallOrderHandler.handleOrder(order3); // Output: "Processing large order for 600"
在此示例中,责任链模式用于处理不同金额的订单。 SmallOrderHandler、MediumOrderHandler 和 LargeOrderHandler 等处理程序各自根据订单金额确定是否可以处理订单。如果可以,他们就会处理它;如果可以的话,他们就会处理它。否则,他们会将订单传递给链中的下一个处理程序。
责任链模式在各种场景中都很有价值,包括:
使用责任链模式时,请考虑以下事项:
访问者模式是一种行为设计模式,允许您将算法与其运行的对象结构分开。它提供了一种向对象添加新操作而无需修改其类的方法,从而可以轻松扩展复杂对象层次结构的功能。当您有一组不同的元素并希望在不修改其代码的情况下对它们执行各种操作时,此模式特别有用。
在 JavaScript 中,您可以使用函数或类来实现访问者模式,以表示访问对象结构中的元素的访问者。让我们通过实际示例探讨如何在 JavaScript 中实现和使用访问者模式。
假设您正在开发一个内容管理系统,其中有不同类型的内容元素,例如文章、图像和视频。您希望对这些元素执行各种操作,例如渲染和导出,而不修改它们的类。您可以使用访客模式:
// Element interface class ContentElement { accept(visitor) {} }
// Concrete Elements class Article extends ContentElement { accept(visitor) { visitor.visitArticle(this); } }class Image extends ContentElement { accept(visitor) { visitor.visitImage(this); } }class Video extends ContentElement { accept(visitor) { visitor.visitVideo(this); } }// Visitor interface class Visitor { visitArticle(article) {} visitImage(image) {} visitVideo(video) {} }// Concrete Visitors class RendererVisitor extends Visitor { visitArticle(article) { console.log(`Rendering article: ${article.title}`); } visitImage(image) { console.log(`Rendering image: ${image.caption}`); } visitVideo(video) { console.log(`Rendering video: ${video.title}`); } }class ExportVisitor extends Visitor { visitArticle(article) { console.log(`Exporting article: ${article.title}`); } visitImage(image) { console.log(`Exporting image: ${image.caption}`); } visitVideo(video) { console.log(`Exporting video: ${video.title}`); } }// Usage const elements = [new Article('Article 1'), new Image('Image 1'), new Video('Video 1')]; const renderer = new RendererVisitor(); const exporter = new ExportVisitor();elements.forEach((element) => { element.accept(renderer); element.accept(exporter); });
在此示例中,我们有文章、图像和视频等内容元素,我们希望在不修改其类的情况下对它们执行渲染和导出操作。我们通过实现访问者类(例如 RendererVisitor 和 ExportVisitor)来访问元素并执行所需的操作来实现此目的。
访客模式在各种场景中都很有价值,包括:
使用访问者模式时,请考虑以下事项:
在对 JavaScript 设计模式的全面探索中,我们深入研究了各种模式,使开发人员能够创建灵活、可维护且高效的代码。每种设计模式都解决特定的问题,并为常见的软件设计挑战提供优雅的解决方案。
我们首先了解设计模式的基本概念,并将它们分为三大类:创建模式、结构模式和行为模式。在每个类别中,我们研究了流行的设计模式并展示了它们在 JavaScript 中的实际实现。
以下是我们所讨论的关键设计模式的简要回顾:
创建模式:这些模式侧重于对象创建机制,包括用于确保类的单个实例的单例模式、用于使用灵活工厂创建对象的工厂和抽象工厂模式、用于逐步构造复杂对象的构建器模式、用于克隆的原型模式对象和对象池模式以实现有效的对象重用。
结构模式:这些模式处理对象组合,提供从更简单的组件构建复杂结构的方法。我们探索了用于适配接口的适配器模式、用于动态向对象添加行为的装饰器模式、用于控制对对象的访问的代理模式、用于将对象组合成树结构的复合模式、用于将抽象与实现分离的桥接模式以及享元模式通过共享公共状态来最小化内存使用的模式。
行为模式:这些模式涉及对象之间的交互和通信。我们涵盖了用于实现分布式事件处理系统的观察者模式、用于封装可互换算法的策略模式、用于将请求转换为独立对象的命令模式、用于基于内部状态管理对象行为的状态模式、用于构建分布式事件处理系统的责任链模式。用于处理请求的处理程序链,以及用于将算法与对象结构分离的访问者模式。
设计模式是开发人员工具包中的宝贵工具,可以创建可扩展且可维护的代码库。在 JavaScript 项目中理解和应用这些模式可以让您编写更高效、适应性更强且健壮的软件。
请记住,设计模式并不是一刀切的解决方案,其适用性取决于项目的具体要求和挑战。仔细考虑何时以及如何应用它们以获得最佳结果。
随着您作为 JavaScript 开发人员的不断成长,掌握这些设计模式将使您能够充满信心和创造力地应对复杂的软件设计挑战。无论您是构建 Web 应用程序、游戏引擎还是任何其他软件,设计模式都将是您编写优雅且可维护的代码的盟友。快乐编码!
也发布在这里。