Abstract Factory Pattern

The Abstract Factory Pattern, also known as the Kit pattern, is a design pattern that abstracts the factory of classes to allow for the creation of product class clusters, rather than just being responsible for creating instances of a certain type of product. The pattern provides an interface for creating a series of related or interdependent objects without specifying their concrete classes. It belongs to the category of object creation patterns.

Description

In the Factory Method Pattern, a specific factory is responsible for producing specific products, with each concrete factory corresponding to a particular product. The factory method also has uniqueness, typically with only one factory method or a group of overloaded factory methods within a specific factory. However, there are cases when a factory needs to provide multiple product objects instead of just a single product object. When the system's factory needs to produce specific products that are not just simple objects, but multiple objects belonging to different product hierarchy levels and types, the Abstract Factory Pattern is used.
The main distinction between the Abstract Factory Pattern and the Factory Method Pattern is that the Factory Method Pattern addresses a single product hierarchy, while the Abstract Factory Pattern needs to handle multiple product hierarchies. A factory hierarchy can be responsible for creating objects from multiple different product hierarchies. When a factory hierarchy can create all objects belonging to a product family in different product hierarchies, the Abstract Factory Pattern is simpler and more efficient than the Factory Method Pattern.

Pattern Structure

  • AbstractFactory: Abstract factory.
  • ConcreteFactory: Concrete factory.
  • AbstractProduct: Abstract product.
  • Product: Concrete product.

Advantages

  • The Abstract Factory Pattern isolates the generation of specific classes, allowing the client to be unaware of what is being created. This isolation makes it relatively easy to switch to a different concrete factory. Since all concrete factories implement the common interfaces defined in the abstract factory, changing the concrete factory instance can to some extent change the behavior of the entire software system. Additionally, applying the Abstract Factory Pattern can achieve the design goal of high cohesion and low coupling, making it widely applicable.
  • When multiple objects in a product family are designed to work together, it ensures that the client always uses objects from the same product family. This is a very practical design pattern for software systems that need to determine their behavior based on the current environment.
  • Adding new concrete factories and product families is convenient and does not require modifying the existing system, adhering to the open-closed principle.

Disadvantages

  • Adding new product objects can be difficult because extending the abstract factory to produce new types of products would involve modifying the interface in the abstract factory role, requiring changes to the abstract factory role and all its subclasses, which is obviously inconvenient.
  • The bias of the open-closed principle: adding new factories and product families is easy, but adding new product hierarchies is troublesome.

Implementation

class Shape { // Base class for shapes
    draw(){
        console.log(this.shape);
    }
}

class Rectangle extends Shape{ // Rectangle
    constructor(){
        super();
        this.shape = "Rectangle";
    }
}

class Square extends Shape{ // Square
    constructor(){
        super();
        this.shape = "Square";
    }
}

class Circle extends Shape{ // Circle
    constructor(){
        super();
        this.shape = "Circle";
    }
}

class Color { // Base class for colors
    fill(){
        console.log(this.color);
    }
}

class Red extends Color{ // Red
    constructor(){
        super();
        this.color = "Red";
    }
}

class Green extends Color{ // Green
    constructor(){
        super();
        this.color = "Green";
    }
}

class Blue extends Color{ // Blue
    constructor(){
        super();
        this.color = "Blue";
    }
}

class Factory{ // Base class for factories mimicking abstract methods
    getShape(){
        throw new Error("Abstract method cannot be called");
    }
    getColor(){
        throw new Error("Abstract method cannot be called");
    }
}
class ShapeFactory extends Factory{ // Shape Factory
    getShape(shape){
        switch (shape.toLowerCase()) {
          case "rectangle":
            return new Rectangle();
          case "square":
            return new Square();
          case "circle":
            return new Circle();
          default:
            throw new Error("Parameter Error");
        }
    }

    getColor(){
        return null;
    }
}

class ColorFactory extends Factory{ // Color Factory
    getShape(){
        return null;
    }

    getColor(color){
        switch (color.toLowerCase()) {
          case "red":
            return new Red();
          case "green":
            return new Green();
          case "blue":
            return new Blue();
          default:
            throw new Error("Parameter Error");
        }
    }
}


class FactoryGenerator{ // Factory Generator Class: Get factory by passing shape or color
    static getFactory(choice){
        choice = choice.toLowerCase();
        if(choice === "shape") {
            return new ShapeFactory();
        }else if(choice === "color"){
            return new ColorFactory();
        }
        throw new Error("Parameter Error");
    }
}

(function(){
    var shapeFactory = FactoryGenerator.getFactory("shape");
    var colorFactory = FactoryGenerator.getFactory("color");

    shapeFactory.getShape("rectangle").draw();
    shapeFactory.getShape("square").draw();
    shapeFactory.getShape("circle").draw();
    colorFactory.getColor("red").fill();
    colorFactory.getColor("green").fill();
    colorFactory.getColor("blue").fill();
})();

Daily Question

https://github.com/WindrunnerMax/EveryDay

Reference

https://www.runoob.com/design-pattern/abstract-factory-pattern.html https://wiki.jikexueyuan.com/project/java-design-pattern/abstract-factory-pattern.html https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/abstract_factory.html