DEV Community

Cover image for JS and Design Patterns - Chapter 5 ๐Ÿš€
devlazar
devlazar

Posted on • Updated on

JS and Design Patterns - Chapter 5 ๐Ÿš€

Table Of Contents
* ๐Ÿค“INTRODUCTION
* ๐ŸญFACTORY STORIES
* ๐Ÿช‘THE FURNITURE FACTORY STORY
* ๐Ÿš—THE VEHICLE-MOTOR FACTORY STORY
* ๐ŸฆTHE ANIMAL FACTORY STORY
* ๐Ÿ‘ฉโ€๐Ÿ’ปGENERIC CODE EXAMPLE
* ๐ŸฏTHE ANIMAL WORLD FACTORY EXAMPLE
* ๐Ÿ’กWHEN TO USE
* โœ…PROS
* โŒCONS
* ๐Ÿ™THANK YOU

๐Ÿค“ INTRODUCTION

Hello, my dear coders! Welcome, to yet another Codespresso JS and Design Patterns blog. I hope you are having a great day, before you get back to hacking, I want to remind you that I am posting Computer Science - Programming right here on DEV.to. You can also follow me, and contact me via ๐ŸคTwitter, LinkedIn or via E-mail. Please, do not hesitate to contact me if you have any ambiguities or just want to say hey. I am here to cooperate, learn from you, maybe learn you something and hang out.

Now, let's get to business. Today, we are discussing the Abstract Factory design pattern. Let's start by looking at this delicious scene of dancing ice creams. ๐Ÿคค

Factory

๐Ÿญ FACTORY STORIES

There are so many stories you could use to describe the Abstract Factory Design Pattern, I will use a couple of the most popular ones. But let's say something about the Abstract Factory pattern, a definition of the sort.

Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.

Abstract Factory suggests defining an interface for creating an object where you allow the subclasses to decide which class to instantiate. This pattern handles the problem by defining a completely separate method for the creation of objects and which sub-classes are able to override so they can specify the 'type' of the factory product that will be created.

THE FURNITURE FACTORY STORY

Let's say that you want to build software that will be used by the furniture shop. You will structure your code so that you have specific classes that will represent of:

  • A family of related products (Chairs, CoffeeTables, DinnerTables...)
  • Several variants of the mentioned family. For example, Chair, CoffeeTables, DinnerTables may be available in different styling variants (Traditional, Casual, Contemporary...)

So, you will need to create individual furniture items so that they match other objects of the same family, but you don't want to change the existing code when adding new products or families of products to the program.

THE VEHICLE-MOTOR FACTORY STORY

For example, a class Vehicle that has a member Motor, but no concrete type of Motor defined in advance, can be constructed by telling the Vehicle constructor to use an electric motor or a gasoline motor. Also, a class Vehicle with a member Motor defined with a dynamic type can have subclasses of type, like an electric plane or an old car, each constructed with a different type of Motor. This can be accomplished by constructing the subclasses with a Vehicle factory method while supplying the motor type.

THE ANIMAL FACTORY STORY

Let's say you want to develop a game, that will have the world, the continents, and you need a generator, that will generate different animal species. In that case, you will have Continent Factory, many concrete continent factories, for example, Africa Factory and America Factory. Then, you can have different categories of animals, for example, Herbivores and Carnivores. Those are Animal Factory classes. Their respected concrete classes could be Lion (Carnivore), Bison(Herbivore), wildebeest (Herbivore), Wolf (Carnivore)...

You see where am I going with this? You can name any example you come up with, comment about it. ๐Ÿ˜Š

Scott

๐Ÿ‘ฉโ€๐Ÿ’ป GENERIC CODE EXAMPLE

Here comes a coding section ๐Ÿ˜‰ AS ALWAYS, READ THE CODE COMMENTS

//Generic Abstract Factory class
      class AbstractFactory {
        //methods for creating products
        createProductA() {
          return;
        }
        createProductB() {
          return;
        }
      }
      //Generic Concrete Factory class that inherits an Abstract Factory Class
      class ConcreteFactory1 extends AbstractFactory {
        //overridden method for create a specific ProductA1 product 
        createProductA() {
          return new ProductA1();
        }
        //overridden method for create a specific ProductB1 product 
        createProductB() {
          return new ProductB1();
        }
      }
      //Generic Concrete Factory class that inherits an Abstract Factory Class
      class ConcreteFactory2 extends AbstractFactory {
        //overridden method for create a specific ProductA2 product 
        createProductA() {
          return new ProductA2();
        }
        //overridden method for create a specific ProductB2 product 
        createProductB() {
          return new ProductB2();
        }
      }
      //Abstract product A class
      class AbstractProductA {}
      //Abstract product B class with a single method that will be overridden
      class AbstractProductB {
        interact(abstractProductA) {}
      }
      //Product A1 inherits AbstractProductA
      class ProductA1 extends AbstractProductA {}
      //Product B1 inherits AbstractProductB implements the interact method
      class ProductB1 extends AbstractProductB {
        interact(abstractProductA) {
          //returns type of the current object (Object) and the type of the function parameter
          return typeof this + " interacts " + typeof abstractProductA;
        }
      }
      //Product A2 inherits AbstractProductA
      class ProductA2 extends AbstractProductA {}
      //Product B2 inherits AbstractProductB implements the interact method
      class ProductB2 extends AbstractProductB {
        interact(abstractProductA) {
          return typeof this + " interacts " + typeof abstractProductA;
        }
      }
      //Client class
      class Client {
        //constructor takes concrete factory class instance
        constructor(abstractFactory) {
          //creating the products
          this.abstractProductB = abstractFactory.createProductB();
          this.abstractProductA = abstractFactory.createProductA();
        }

        //example of product interaction
        run() {
          return this.abstractProductB.interact(this.abstractProductA);
        }
      }

      var factory_1 = new ConcreteFactory1();
      var client_1 = new Client(factory_1);
      console.log(
        "%c%s",
        "color: black; background: lightgreen; font-size: 24px;  border: 1px solid lightgreen; border-radius: 5px; padding: 5px;",
        "Result: " + client_1.run()
      );

      var factory_2 = new ConcreteFactory2();
      var client_2 = new Client(factory_2);
      console.log(
        "%c%s",
        "color: black; background: lightgreen; font-size: 24px; border: 1px solid lightgreen; border-radius: 5px; padding: 5px;",
        "Result: " + client_2.run()
      );
Enter fullscreen mode Exit fullscreen mode

This is a generic example of the Abstract Factory Design Pattern, I will include the visual representation for the visual learners.

Abstract Factory

๐Ÿฏ THE ANIMAL WORLD FACTORY EXAMPLE

I mentioned the Animal Factory Story earlier. The best thing is that with just slightly changing the code we can apply the Abstract Factory Pattern to implement that solution. The difference between the code below and the previous code is just that I added a constructor that will initialize the name of the class so we could use it when printing the data in the console. Even the comments are the same, nothing actually changed, except the story. ๐ŸŽ‰

//Generic Abstract Factory class
      class ContinentFactory {
        //methods for creating products
        createHerbivore() {
          return;
        }
        createCarnivore() {
          return;
        }
      }
      class AfricaFactory extends ContinentFactory {
        //overridden method for create a specific ProductA1 product
        createHerbivore() {
          return new Wildebeest();
        }
        //overridden method for create a specific ProductB1 product
        createCarnivore() {
          return new Lion();
        }
      }
      //Generic Concrete Factory class that inherits an Abstract Factory Class
      class AmericaFactory extends ContinentFactory {
        //overridden method for create a specific ProductA2 product
        createHerbivore() {
          return new Bison();
        }
        //overridden method for create a specific ProductB2 product
        createCarnivore() {
          return new Wolf();
        }
      }
      //Abstract product A class
      class Herbivore {}
      //Abstract product B class with a single method that will be overridden
      class Carnivore {
        eat(herbivore) {}
      }
      //Product A1 inherits AbstractProductA
      class Wildebeest extends Herbivore {
        constructor() {
          super();
          this.name = "Wildebeest";
        }
      }
      //Product B1 inherits AbstractProductB implements the interact method
      class Lion extends Carnivore {
        constructor() {
          super();
          this.name = "Lion";
        }
        eat(herbivore) {
          //returns type of the current object (Object) and the type of the function parameter
          return this.name + " eats " + herbivore.name;
        }
      }
      //Product A2 inherits AbstractProductA
      class Bison extends Herbivore {
        constructor() {
          super();
          this.name = "Bison";
        }
      }
      //Product B2 inherits AbstractProductB implements the interact method
      class Wolf extends Carnivore {
        constructor() {
          super();
          this.name = "Wolf";
        }
        eat(herbivore) {
          return this.name + " eats " + herbivore.name;
        }
      }
      //Client class
      class AnimalWorld {
        //constructor takes concrete factory class instance
        constructor(continent) {
          //creating the products
          this.carnivore = continent.createCarnivore();
          this.herbivore = continent.createHerbivore();
        }

        //example of product interaction
        start() {
          return this.carnivore.eat(this.herbivore);
        }
      }

      var africa = new AfricaFactory();
      var animalWorld = new AnimalWorld(africa);
      console.log(
        "%c%s",
        "color: black; background: lightgreen; font-size: 24px;  border: 1px solid lightgreen; border-radius: 5px; padding: 5px;",
        "Result: " + animalWorld.start()
      );

      //Output: Lion eats Wildebeest

      var america = new AmericaFactory();
      var animalWorld_2 = new AnimalWorld(america);
      console.log(
        "%c%s",
        "color: black; background: lightgreen; font-size: 24px; border: 1px solid lightgreen; border-radius: 5px; padding: 5px;",
        "Result: " + animalWorld_2.start()
      );

      //Output: Wolf eats Bison
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก WHEN TO USE THE ABSTRACT FACTORY DESIGN PATTERN

  • Use the Abstract Factory Design Pattern when your code needs to work with various families of related products, but you don't want it to depend on the concrete classes of those products - they might be unknown beforehand or you simply want to allow for future extensibility.

โœ… PROS

-You can be sure that the products youโ€™re getting from a factory are compatible with each other.
-You avoid tight coupling between concrete products and client code.
-Single Responsibility Principle. You can extract the product creation code into one place, making the code easier to support.
-Open/Closed Principle. You can introduce new variants of products without breaking existing client code.

โŒ CONS

-The code may become more complicated than it should be since a lot of new interfaces and classes are introduced along with the pattern.

๐Ÿ™ THANK YOU FOR READING!

References:
School notes...
refactoring

Please leave the comment, tell me about you, about your work, comment your thoughts, connect with me via Twitter or LinkedIn.

โ˜• SUPPORT ME AND KEEP ME FOCUSED!
Buy Me a Coffee at ko-fi.com

Have a nice time hacking! ๐Ÿ˜Š

Oldest comments (0)