Factory

Creation of objects that conform to the same interface without specifying concrete classes


namespace WhoNeedsAFactory {
  class ToyCar {
    whatIsIt(): void {
      console.log("this is a toy car");
    }

    play(): void {
      console.log("vroom!!");
    }
  }

  class ToyProductionPlant {
    makePresents(): ToyCar[] {
      let carA = new ToyCar();
      let carB = new ToyCar();
      let carC = new ToyCar();
      return new Array(carA, carB, carC);
    }

    makeAToy(): ToyCar {
      return new ToyCar();
    }
  }

  let tpp = new ToyProductionPlant();
  let car = tpp.makePresents();
  car.forEach((x) => x.play());
}

//output:
vroom!!
vroom!!
vroom!!
  

The code above demonstrates how a simple toy factory can make the only toy they know about -- a car. But what if they learned to create other toys?

First, let's extract something in common from all elements so that we can operate on an abstraction and not a concrete entity


  interface Present {
    whatIsIt(): string;
    play(): string;
  }

  

Then let's define concrete classes and ensure they implement the common interface:


  class ToyCar implements Present {
    whatIsIt(): string {
      return "This is a toy car";
    }
    play(): string {
      return "vroom!!";
    }
  }

  class ToyBoat implements Present {
    play(): string {
      return "Off we go!";
    }
    whatIsIt(): string {
      return "This is a toy boat";
    }
  }

  class BagOfCoal implements Present {
    play(): string {
      return "You now have a dirty face!";
    }
    whatIsIt(): string {
      return "Bag of coal";
    }
  }
    

Next, the Creator class, this is where the magic of extensibility happens:


  enum ToyType {
    car,
    boat,
    coal,
  }
  //Creator class
  class ToyFactory {
    makeAToy(typeOfToy: ToyType): Present {
      switch (typeOfToy) {
        case ToyType.boat: {
          return new ToyBoat();
        }
        case ToyType.car: {
          return new ToyCar();
        }
        case ToyType.coal: {
          return new BagOfCoal();
        }
      }
    }
  }

  

Finally, the wire-up:



  class ElvesFactory {
    toyFactory: ToyFactory;

    constructor(toyFactory: ToyFactory) {
      this.toyFactory = toyFactory;
    }

    makeToys(order: ToyType[]) {
      order.forEach((x) => {
        let toy = this.toyFactory.makeAToy(x);
        console.log(toy.whatIsIt());
        console.log(toy.play());
      });
    }
  }
  

Test:



  //check:
  let tf = new ToyFactory();
  let toyProductionFacility = new ElvesFactory(tf);
  let order = new Array(
    ToyType.boat,
    ToyType.car,
    ToyType.boat,
    ToyType.car,
    ToyType.coal,
    ToyType.car
  );
  toyProductionFacility.makeToys(order);

  //output:
    This is a toy boat
    Off we go!

    This is a toy car
    vroom!!

    This is a toy boat
    Off we go!

    This is a toy car
    vroom!!

    Bag of coal
    You now have a dirty face!

    This is a toy car
    vroom!!
  
If elves learn to make more toys, there will need to be a class created for that toy that implements the Present interface, add a value to the enum, and then a condition to create that type of toy into the creator class.