JS and Design Patterns - Chapter 5 π
devlazar
Posted on January 25, 2021
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 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. π
π©βπ» 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()
);
This is a generic example of the Abstract Factory Design Pattern, I will include the visual representation for the visual learners.
π― 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
π‘ 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!
Have a nice time hacking! π
Posted on January 25, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.