Design Pattern: Strategy Pattern in TypeScript

Strategy pattern makes it easier to manage algorithms with the same outline(serving the same purpose in different rule/implementation). This pattern encapsulates the algorithms and enables the client to use them as plugins.

This article demonstrates Strategy pattern implementations in TypeScript. Check the following examples.

Implementation

Here are the steps for strategy pattern implementation in TypeScript-

Here is a simple example of Strategy pattern-

// Strategy pattern implementation in TypeScript

// Strategy interface
interface IStrategy {
    operation1(): void;
    operation2(): void;
}

// First concrete strategy
class ConcreteStrategyOne implements IStrategy {
    operation1(): void {
        console.log("Strategy One: operation 1");
    }

    operation2(): void {
        console.log("Strategy One: operation 2");
    }
}

// Second concrete strategy
class ConcreteStrategyTwo implements IStrategy {
    operation1(): void {
        console.log("Strategy Two: operation 1");
    }

    operation2(): void {
        console.log("Strategy Two: operation 2");
    }
}

// Strategy class
class Strategy {
    private readonly strategy: IStrategy;

    constructor(storage: IStrategy) {
        this.strategy = storage;
    }

    someOperation1(): void {
        // perform some operation as per requirement (if required)

        this.strategy.operation1();

        // perform some operation as per requirement (if required)
    }

    someOperation2(): void {
        // perform some operation as per requirement (if required)

        this.strategy.operation2();

        // perform some operation as per requirement (if required)
    }

    someComplexOperation(): void {
        // perform some operation as per requirement (if required)

        this.strategy.operation2();

        // perform some operation as per requirement (if required)

        this.strategy.operation1();

        // perform some operation as per requirement (if required)
    }
}

// Demo
console.log("Using Strategy One");

let strategy = new Strategy(new ConcreteStrategyOne());
strategy.someOperation1();


console.log("Using Strategy Two");

strategy = new Strategy(new ConcreteStrategyTwo());
strategy.someOperation1();
strategy.someOperation2();
strategy.someComplexOperation();

Output will be as below-

Using Strategy One
Strategy One: operation 1

Using Strategy Two
Strategy Two: operation 1
Strategy Two: operation 2
Strategy Two: operation 2
Strategy Two: operation 1

Examples

Here are a few examples of the Strategy pattern implementation in TypeScript-

Example #1: Transport

Let’s take the example of a transport system.

Transport Interface

// transport.ts

interface Transport {
    start(): void;
    stop(): void;
    repair(): void;
    getInfo(): void;
    operate(): void;
}

export default Transport;

Transport Implementation for Bike

// bike.ts

import Transport from "./transport";

class Bike implements Transport {
    start(): void {
        console.log("Bike started");
    }
    
    stop(): void {
        console.log("Bike stopped");
    }

    repair(): void {
        console.log("Bike repair");
    }

    getInfo(): void {
        console.log("Transport type: Bike");
        console.log("Number of wheels: 2");
        console.log("Average Weight: 700 Pounds");
    }

    operate(): void {
        console.log("Riding Bike ............");
    }
}

export default Bike;

Transport Implementation for Car

// car.ts

import Transport from "./transport";

class Car implements Transport {
    start(): void {
        console.log("Car started");
    }

    stop(): void {
        console.log("Car stopped");
    }

    repair(): void {
        console.log("Car repair");
    }

    getInfo(): void {
        console.log("Transport type: Car");
        console.log("Number of wheels: 4");
        console.log("Average Weight: 4,000 Pounds");
    }

    operate(): void {
        console.log("Driving car ............");
    }
}

export default Car;

Transport Implementation for Plane

// plane.ts

import Transport from "./transport";


class Plane implements Transport {
    start(): void {
        console.log("Plane started");
    }

    stop(): void {
        console.log("Plane stopped");
    }

    repair(): void {
        console.log("Plane repair");
    }

    getInfo(): void {
        console.log("Transport type: Plane");
        console.log("Number of wheels: 3");
        console.log("Average Weight: 50,000 Pounds");
    }

    operate(): void {
        console.log("Flying plane ............");
    }
}

export default Plane;

Transport Strategy Class

// transport-strrategy.ts

import Transport from "./transport";

class TransportStrategy {
    private transport: Transport;

    constructor(transport: Transport) {
        this.transport = transport;
    }

    execute(): void {
        this.transport.start();

        this.transport.getInfo();

        this.transport.operate();
    }

    repair(): void {
        this.transport.repair();
    }

    stop(): void {
        this.transport.stop();
    }
}

export default TransportStrategy;

Demo

// demo.ts

import Bike from "./bike";
import Car from "./car";
import Plane from "./plane";
import TransportStrategy from "./transport-strategy";


// Use bike
console.log("Operating Bike:");

let myTransport = new TransportStrategy(new Bike());
myTransport.execute();
myTransport.stop();

// Use car
console.log("\n\nOperating Car:");

myTransport = new TransportStrategy(new Car());
myTransport.execute();
myTransport.stop();
myTransport.repair();

// Use plane
console.log("\n\nOperating plane:");

myTransport = new TransportStrategy(new Plane());
myTransport.execute();
myTransport.stop();

Output

Operating Bike:
Bike started
Transport type: Bike
Number of wheels: 2
Average Weight: 700 Pounds
Riding Bike ............
Bike stopped


Operating Car:
Car started
Transport type: Car
Number of wheels: 4
Average Weight: 4,000 Pounds
Driving car ............
Car stopped
Car repair


Operating plane:
Plane started
Transport type: Plane
Number of wheels: 3
Average Weight: 50,000 Pounds
Flying plane ............
Plane stopped

Example #2: Mathematical Calculations

Calculation Interface

// calculation.ts

interface Calculation {
    execute(num1: number, num2: number): number;
}

export default Calculation;

Calculation Implementation for Add

// add.ts

import Calculation from "./calculation";

class Add implements Calculation {
    execute(num1: number, num2: number): number {
        return num1 + num2;
    }
}

export default Add;

Calculation Implementation for Subtract

// subtract.ts

import Calculation from "./calculation";

class Subtract implements Calculation {
    execute(num1: number, num2: number): number {
        return num1 - num2;
    }
}

export default Subtract;

Calculation Implementation for Multiply

// multiply.ts

import Calculation from "./calculation";

class Multiply implements Calculation {
    execute(num1: number, num2: number): number {
        return num1 * num2;
    }
}

export default Multiply;

Calculation Implementation for Divide

// divide.ts

import Calculation from "./calculation";

class Divide implements Calculation {
    execute(num1: number, num2: number): number {
        return num1 / num2;
    }
}

export default Divide;

Calculation Strategy Class

// calculation-strategy.ts

import Calculation from "./calculation";

class CalculationStrategy {
    private calculation: Calculation;

    constructor(calculation: Calculation) {
        this.calculation = calculation;
    }

    execute(num1: number, num2: number): number {
        return this.calculation.execute(num1, num2);
    }
}

export default CalculationStrategy;

Demo

// demo.ts

import Add from "./add";
import Divide from "./divide";
import Multiply from "./multiply";
import Subtract from "./subtract";
import CalculationStrategy from "./calculation-strategy";


console.log("Performing operation: ADD");
var myCalculation = new CalculationStrategy(new Add());
var result = myCalculation.execute(10, 5);
console.log("10 + 5 = " + result);

console.log("\n\nPerforming operation: SUBTRACT");
myCalculation = new CalculationStrategy(new Subtract());
result = myCalculation.execute(10, 5);
console.log("10 - 5 = " + result);

console.log("\n\nPerforming operation: MULTIPLY");
myCalculation = new CalculationStrategy(new Multiply());
result = myCalculation.execute(10, 5);
console.log("10 * 5 = " + result);

console.log("\n\nPerforming operation: DIVIDE");
myCalculation = new CalculationStrategy(new Divide());
result = myCalculation.execute(10, 5);
console.log("10 / 5 = " + result);

Result

Performing operation: ADD
10 + 5 = 15


Performing operation: SUBTRACT
10 - 5 = 5


Performing operation: MULTIPLY
10 * 5 = 50


Performing operation: DIVIDE
10 / 5 = 2

Example #3: File Storage

Define Storage Interface

// storage-system.ts

interface StorageSystem {
    storeFile(tempPath: string): number;
    retrieveFile(fileId: number): string;
    printFileInfo(fileId: number): void;
}

export default StorageSystem;

Implement Storage Interface for Local Storage

// local-storage.ts

import StorageSystem from "./storage-system";

class LocalStorage implements StorageSystem {

    storeFile(tempPath: string): number {
        // Code to store file here

        // return the ID that is obtained from the database record or any other unique identifier.

        // For demo purpose a random number is returned here
        return Math.floor(Math.random() * 100);
    }

    retrieveFile(fileId: number): string {
        // Retrieve the url and return back

        // Some dummy url is returned for demo purpose
        return "https://bigboxcode.com/files/local/" + fileId;
    }

    printFileInfo(fileId: number): void {
        console.log("Storage type: Local Storage");
        console.log("File ID: " + fileId);
        console.log("File URL: " + this.retrieveFile(fileId));
    }
}

export default LocalStorage;

Implement Storage Interface for Google Drive

// google-drive-storage.ts

import StorageSystem from "./storage-system";


class GoogleDriveStorage implements StorageSystem {

    storeFile(tempPath: string): number {
        // Code to store file here

        // return the ID that is obtained from the database record or any other unique identifier.

        // For demo purpose a random number is returned here
        return Math.floor(Math.random() * 1000);
    }


    retrieveFile(fileId: number): string {
        // Retrieve the url and return back

        // Some dummy url is returned for demo purpose
        return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view";
    }

    printFileInfo(fileId: number): void {
        console.log("Storage type: Google Drive");
        console.log("File ID: " + fileId);
        console.log("File URL: " + this.retrieveFile(fileId));
    }
}

export default GoogleDriveStorage;

Implement Storage Interface for AWS S3

// s3-storage.ts

import StorageSystem from "./storage-system";

class S3Storage implements StorageSystem {

    storeFile(tempPath: string): number {
        // Code to store file here

        // return the ID that is obtained from the database record or any other unique identifier.

        // For demo purpose a random number is returned here
        return Math.floor(Math.random() * 10000);
    }

    retrieveFile(fileId: number): string {
        // Retrieve the url and return back

        // Some dummy url is returned for demo purpose
        return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf";
    }

    printFileInfo(fileId: number): void {
        console.log("Storage type: AWS S3");
        console.log("File ID: " + fileId);
        console.log("File URL: " + this.retrieveFile(fileId));
    }
}

export default S3Storage;

Storage Strategy Class

// storage-strategy.ts

import StorageSystem from "./storage-system";

class StorageStrategy {
    private storage: StorageSystem;

    constructor(storage: StorageSystem) {
        this.storage = storage;
    }

    uploadFile(tempPath: string): number {
        const fileId = this.storage.storeFile(tempPath);
        this.storage.printFileInfo(fileId);

        return fileId;
    }

    getFileUrl(fileId: number): string {
        return this.storage.retrieveFile(fileId);
    }
}

export default StorageStrategy;

Demo

// demo.ts

import GoogleDriveStorage from "./google-drive-storage";
import LocalStorage from "./local-storage";
import S3Storage from "./s3-storage";
import StorageStrategy from "./storage-strategy";


// Use Local storage
console.log("Using local storage:");

let fileStorage = new StorageStrategy(new LocalStorage());
fileStorage.uploadFile("/some-temp-path");

// Use car
console.log("\n\nUsing AWS S3:");

fileStorage = new StorageStrategy(new S3Storage());
fileStorage.uploadFile("/some-temp-path");

// Use plane
console.log("\n\nUsing Google Drive:");

fileStorage = new StorageStrategy(new GoogleDriveStorage());
fileStorage.uploadFile("/some-temp-path");

Output

Using local storage:
Storage type: Local Storage
File ID: 20
File URL: https://bigboxcode.com/files/local/20


Using AWS S3:
Storage type: AWS S3
File ID: 9420
File URL: https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf


Using Google Drive:
Storage type: Google Drive
File ID: 502
File URL: https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Strategy pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.