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)