Try   HackMD
tags: design pattern

Design Pattern - Decorator

Decorator design pattern is nothing about the syntax of decorator. It is just a pattern of organize the code in case that we want to extend one or more feature base on the existing object.

Taking an example if we have an restaurant which have many menu. We can use decorator pattern to
set a base meal class, instanciate it and wrap many option around it.

This is much better than setting a list of properties in the meal class, check if the certain properties are met and then calc the condition.

Lets dive into the code to see the implimentation.

abstract class MealSet {
  basePrice = 20;
  description = "Serve the water & utensils";
  abstract getPrice(): number;
  abstract getDescription(): string;
  abstract getMainDish(): string;
}

Firstly, se set a abstract meal set class with base price and description

class FishCousin extends MealSet {
  getPrice(): number {
    return this.basePrice + 20;
  }
  getDescription(): string {
    return `1. ${this.description}\n2. Main: Fish`;
  }
  getMainDish(): string {
    return "Fish";
  }
}
class PorksCousin extends MealSet {
  getPrice(): number {
    return this.basePrice + 10;
  }
  getDescription(): string {
    return `1. ${this.description}\n2. Main: Pork`;
  }
  getMainDish(): string {
    return "Pork";
  }
}

We then inheit the abstract basic meal set with real options pork meal and fish meal with their own price and description.

abstract class MealStyling extends MealSet {
  decorator: MealSet;
  constructor(meal: MealSet) {
    super();
    this.decorator = meal;
  }
  abstract getCookingMethods(): string;
  abstract getPrice(): number;
}

We can make it even further to make one more abstract class called Meal style to provide cousin style by inheirt the meal set class and passing the instanciated meal (fish or pork)

class JapaneseStyle extends MealStyling {
  getDescription(): string {
    return `Japanese style ${this.decorator.getMainDish()}`;
  }
  getMainDish(): string {
    return this.decorator.getMainDish();
  }
  getCookingMethods() {
    return `${this.getMainDish()} cooked with soy sauce and wasabi`;
  }
  getPrice(): number {
    return this.decorator.getPrice() + 10;
  }
}

class ItalianStyle extends MealStyling {
  getDescription(): string {
    return `Italian style ${this.decorator.getMainDish()}`;
  }
  getMainDish(): string {
    return this.decorator.getMainDish();
  }
  getCookingMethods() {
    return `${this.getMainDish()} cooked with wine`;
  }
  getPrice(): number {
    return this.decorator.getPrice() + 30;
  }
}

Finally, we can just simply see the result of our code

let myLunchCourse = new FishCousin();
let myLunchStyle = new JapaneseStyle(myLunchCourse);

console.log(myLunchCourse.getPrice(), myLunchCourse.getDescription());
console.log(
  myLunchStyle.getPrice(),
  myLunchStyle.getDescription(),
  myLunchStyle.getCookingMethods()
);