Strategy

The strategy pattern (a.k.a policy pattern) allows us to write client code that determines which algorithm to select at runtime.

  • instead of implementing a single algoritm, we implement multiple, and based on some condition, select which algorithm to run.
  • doing this allows the calling code to be more flexible and reusable.

In the following example, we have some client code which needs to update the fields of a food item. The thing each, there are different classes of food items (those that are organic, those that are non-perishable, etc), and they all need to be handled differently. For instance, organic items degrade twice as fast, while non-perishable items don't degrade at all.

export interface FoodItemStrategy {
  update(item: FoodItem): void;
}

export class PerishableFoodItemStrategy implements FoodItemStrategy {
  update(item: FoodItem): void {
    if (item.doesItemImproveWithAge()) {
      item.increaseQuality();
    } else if (item.getSellInDaysValue() <= 0) {
      item.decreaseQuality(2);
    } else {
      item.decreaseQuality();
    }
    item.decrementSellIn();
  }
}

export class OrganicFoodItemStrategy implements FoodItemStrategy {
  update(item: FoodItem): void {
    if (item.getSellInDaysValue() <= 0) {
      item.decreaseQuality(4);
    } else {
      item.decreaseQuality(2);
    }
    item.decrementSellIn();
  }
}

export class NonPerishableFoodItemStrategy implements FoodItemStrategy {
  update(item: FoodItem): void {
    // no-op
  }
}

/* Implementation */
export class StoreInventory {
  constructor(public items: FoodItem[]) {}

  private updateFoodItem(item: FoodItem): void {
    let strategy: FoodItemStrategy;
    if (item.isItemNonPerishable()) {
      strategy = new NonPerishableFoodItemStrategy();
    } else if (item.isItemOrganic()) {
      strategy = new OrganicFoodItemStrategy();
    } else {
      strategy = new PerishableFoodItemStrategy();
    }
    strategy.update(item);
  }
}