DEV Community

Cover image for Mastering the Strategy Pattern with a Car Example πŸš—βš™οΈ
Dhanasai Tholeti
Dhanasai Tholeti

Posted on

Mastering the Strategy Pattern with a Car Example πŸš—βš™οΈ

In the world of software design, flexibility and maintainability are crucial. One of the best ways to achieve this is through the Strategy Patternβ€”a behavioral design pattern that allows us to define a family of algorithms, encapsulate each one, and make them interchangeable.

Let’s break it down using a car example! 🚘

What is the Strategy Pattern?

The Strategy Pattern enables an object’s behavior to be selected at runtime. Instead of hardcoding logic inside an object, we delegate it to separate strategy classes that encapsulate different behaviors. This promotes the Open-Closed Principleβ€”where code is open for extension but closed for modification.


Real-World Example: Cars and Their Behaviors πŸš—βš‘οΈβ›½

Consider a car that can have different transmission types (manual, automatic, semi-automatic) and fuel types (gasoline, diesel, electric). Instead of tightly coupling the car’s logic to these behaviors, we use the Strategy Pattern to define separate classes for each.

πŸ”Ή Implementing Transmission Strategies

Each transmission type is an independent class that implements the TransmissionStrategy interface:

export interface TransmissionStrategy {
  changeGear(): void;
}

export class ManualTransmission implements TransmissionStrategy {
  changeGear(): void {
    console.log("Changing gear manually.");
  }
}

export class AutomaticTransmission implements TransmissionStrategy {
  changeGear(): void {
    console.log("Changing gear automatically.");
  }
}

export class SemiAutomaticTransmission implements TransmissionStrategy {
  changeGear(): void {
    console.log("Changing gear semi-automatically.");
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Implementing Fuel Strategies

Similarly, different fuel types adhere to a FuelStrategy interface:

export interface FuelStrategy {
  refuel(): void;
}

export class Gasoline implements FuelStrategy {
  refuel(): void {
    console.log("Refueling gasoline...");
  }
}

export class Diesel implements FuelStrategy {
  refuel(): void {
    console.log("Refueling diesel...");
  }
}

export class Electric implements FuelStrategy {
  refuel(): void {
    console.log("Charging the battery...");
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή The Car Class Using Strategies

The Car class now delegates behavior to the appropriate strategy:

export class Car {
  private transmission: TransmissionStrategy;
  private fuel: FuelStrategy;

  constructor(transmission: TransmissionStrategy, fuel: FuelStrategy) {
    this.transmission = transmission;
    this.fuel = fuel;
  }

  setTransmission(transmission: TransmissionStrategy): void {
    this.transmission = transmission;
  }

  setFuel(fuel: FuelStrategy): void {
    this.fuel = fuel;
  }

  refuel(): void {
    this.fuel.refuel();
  }

  changeGear(): void {
    this.transmission.changeGear();
  }
}
Enter fullscreen mode Exit fullscreen mode

Why Use the Strategy Pattern? πŸ€”

βœ… Flexibility: We can change the car’s transmission and fuel type dynamically at runtime.

βœ… Encapsulation: Each behavior is encapsulated separately, making it easy to add new strategies.

βœ… Maintainability: Avoids complex if-else conditions, making the code clean and modular.

βœ… Reusability: Strategies can be reused across different contexts without modifications.


Example Usage πŸš€

Now, let’s create different cars and modify their behaviors at runtime:

const manualCar = new Car(new ManualTransmission(), new Gasoline());
manualCar.changeGear(); // Output: Changing gear manually.

const automaticCar = new Car(new AutomaticTransmission(), new Electric());
automaticCar.changeGear(); // Output: Changing gear automatically.

const semiAutomaticCar = new Car(new SemiAutomaticTransmission(), new Diesel());
semiAutomaticCar.changeGear(); // Output: Changing gear semi-automatically.

manualCar.setTransmission(new AutomaticTransmission()); // Switching to automatic
semiAutomaticCar.setFuel(new Electric()); // Switching to electric

manualCar.changeGear(); // Output: Changing gear automatically.
semiAutomaticCar.refuel(); // Output: Charging the battery...
Enter fullscreen mode Exit fullscreen mode

Final Thoughts πŸ’‘

The Strategy Pattern is a powerful tool for designing flexible and reusable software. By decoupling behaviors into independent strategies, we make our applications more adaptable to change.

If you found this insightful, drop a like and share this with your network! πŸš€πŸ’¬

Top comments (0)