Factory Method Pattern

The Factory Method Pattern, also known as the Factory Pattern, Virtual Constructor Pattern, or Polymorphic Factory Pattern, belongs to the category of class creation patterns. In the Factory Method Pattern, the factory parent class is responsible for defining the public interface for creating product objects, while the factory subclass is responsible for generating specific product objects. The purpose of this approach is to defer the instantiation of the product class to the factory subclass, allowing the determination of which specific product class to instantiate through the factory subclass.

Description

The Simple Factory Pattern has a drawback where the creation of classes depends on the factory class. In other words, to extend the program, modifications to the factory class are necessary. The Factory Method Pattern delegates the specific creation process to dedicated factory subclasses, thereby delaying the instantiation of the product class to the factory subclass. Consequently, there is no need to modify the factory class, only the definition of the subclass implementing the factory is required.

Pattern Analysis

The Factory Method Pattern is an abstract and extended version of the Simple Factory Pattern. By utilizing the polymorphism of object-oriented programming, the Factory Method Pattern preserves the advantages of the Simple Factory Pattern while overcoming its limitations. In the Factory Method Pattern, the core factory class no longer assumes responsibility for creating all products, but delegates the specific creation work to its subclasses. The core class is only accountable for providing the interface that concrete factories must implement, and does not concern itself with the details of instantiating a specific product class, allowing the introduction of new products without modifying the factory role.

Pattern Structure

  • Product: Abstract product.
  • ConcreteProduct: Specific product.
  • Factory: Abstract factory.
  • ConcreteFactory: Specific factory.

Advantages

  • In the Factory Method Pattern, the factory method is used to create the products needed by the client, while concealing the details of which specific product class will be instantiated, thus allowing the user to focus solely on the factory corresponding to the required product, without concerning themselves with creation details or even needing to know the class name of the specific product.
  • The key to the Factory Method Pattern lies in the polymorphic design of the factory role and product role. It allows the factory to autonomously determine which product object to create, while completely encapsulating the specifics of creating the object within the concrete factory. Another advantage of using the Factory Method Pattern is its extensibility; introducing a new product to the system does not require modifications to the abstract factory, abstract product interfaces, the client, or any other concrete factories or products. Only the addition of a specific factory and product is necessary. This enhances the system's extensibility, fully compliant with the "open-closed principle."

Disadvantages

  • When adding new products, new concrete product classes need to be written, along with corresponding concrete factory classes, leading to an increase in the number of classes in the system, thereby increasing the system's complexity, as more classes require compilation and execution, resulting in additional overhead for the system.
  • Due to the consideration of system extensibility, an abstract layer needs to be introduced, leading to the use of abstract layers in client code definitions, thereby increasing the system's abstraction and complexity, potentially requiring the use of reflection and other techniques, ultimately increasing the system's implementation complexity.

Implementation

The original intention of the Factory Method Pattern is to delegate the actual object creation work to the subclass, converting the core class into an abstract class. However, in JavaScript, it is challenging to implement abstract classes as in traditional object-oriented programming. Therefore, in JavaScript, it suffices to refer to its core concept.

class Shape { // Base product class
    say() {
        console.log(this.name);
    }
}

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

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

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

class Factory { // Base factory class
    getProduct() {}
}

class RectangleFactory extends Factory {
    constructor() {
        super();
    }

    getProduct() {
        return new Rectangle();
    }
}

class SquareFactory extends Factory {
    constructor() {
        super();
    }

    getProduct() {
        return new Square();
    }
}

class CircleFactory extends Factory {
    constructor() {
        super();
    }

    getProduct() {
        return new Circle();
    }
}

var rectangle = (new RectangleFactory).getProduct();
rectangle.say(); // Rectangle
var square = (new SquareFactory).getProduct();
square.say(); // Square

var circle = (new CircleFactory).getProduct();
circle.say(); // Circle
// Example of assembling shapes // If you need to assemble three shapes
class Shape { // Base class for shapes
    say(){
        console.log(this.name);
    }
}

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

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

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

class Combination{
    constructor(rectangle, square, circle){
        console.log(`Combination: ${rectangle.name} ${square.name} ${circle.name}`);
    }
}

// Direct assembly: the caller only needs a complete combination of shapes, but to assemble them, three shapes need to be instantiated separately, resulting in high coupling.
(function(){
    var rectangle = new Rectangle();
    var square = new Square();
    var circle = new Circle();
    new Combination(rectangle, square, circle);
})();

// Using a factory: hides specific implementation details and reduces coupling
class CombinationFactory{
    getProduct(){
        var rectangle = new Rectangle();
        var square = new Square();
        var circle = new Circle();
        return new Combination(rectangle, square, circle); 
    }
}

(function(){
   var combination = (new CombinationFactory).getProduct(); 
})();

Daily Question

https://github.com/WindrunnerMax/EveryDay

Reference

https://blog.csdn.net/carson_ho/article/details/52343584 https://wiki.jikexueyuan.com/project/java-design-pattern/factory-pattern.html https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/factory_method.html