Strategy Pattern in Node.js

The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly within a class, the pattern uses composition to delegate the execution to one of several possible algorithms that are interchangeable. This is particularly useful when you have multiple ways of doing something, and you want to decide the best way to do it at runtime, depending on the context.

Here’s an example of the Strategy Pattern implemented in Node.js. In this scenario, we’ll create a simple text formatting application that can format text in different ways (e.g., plain text, HTML, or Markdown) based on the strategy chosen at runtime.

Step 1: Define the Strategy Interface

First, we define a strategy interface. In JavaScript, interfaces are not explicit, but we can use function signatures to enforce them. Each strategy will have a format function.

// Strategy interface
class TextFormatStrategy {
format(text) {
throw new Error("Method 'format()' must be implemented.");
}
}

Step 2: Implement Concrete Strategies

Next, we implement concrete strategies that adhere to the TextFormatStrategy interface. Each of these strategies will format the text in a different way.

// PlainTextFormatStrategy.js
class PlainTextFormatStrategy extends TextFormatStrategy {
format(text) {
return text;
}
}
// HtmlFormatStrategy.js
class HtmlFormatStrategy extends TextFormatStrategy {
format(text) {
return <p>${text}</p>;
}
}
// MarkdownFormatStrategy.js
class MarkdownFormatStrategy extends TextFormatStrategy {
format(text) {
return **${text}**;
}
}

Step 3: Context Class

The context class is used to interact with the strategies. It allows for changing the strategy at runtime.

// TextFormatter.js
class TextFormatter {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
format(text) {
return this.strategy.format(text);
}
}

Step 4: Using the Strategy Pattern

Finally, you can use the strategy pattern by instantiating the TextFormatter with a specific strategy and then changing the strategy at runtime as needed.

const plainTextFormatter = new TextFormatter(new PlainTextFormatStrategy());
console.log(plainTextFormatter.format("Hello, World!")); // Output: Hello, World!
const htmlFormatter = new TextFormatter(new HtmlFormatStrategy());
console.log(htmlFormatter.format("Hello, World!")); // Output:
Hello, World!

const markdownFormatter = new TextFormatter(new MarkdownFormatStrategy());
console.log(markdownFormatter.format("Hello, World!")); // Output: Hello, World!
// Dynamically changing strategy
const textFormatter = new TextFormatter(new PlainTextFormatStrategy());
console.log(textFormatter.format("Dynamic Strategy")); // Plain text output
textFormatter.setStrategy(new HtmlFormatStrategy());
console.log(textFormatter.format("Dynamic Strategy")); // HTML output

This example demonstrates how the Strategy Pattern allows for the flexible and interchangeable execution of algorithms, enabling the selection of the appropriate algorithm at runtime based on the context or requirements.