There are three kinds of factory patterns: simple factory pattern, factory method pattern and abstract factory pattern. The simple factory pattern is the most simple and practical pattern. Using factory objects to decide which product class instances to create actually violates the OCP principle. In order to make up for the deficiency of simple factory mode, the factory method mode is proposed, which is extended by adding new factory class to meet the OCP principle. Abstract factory pattern is the integration of simple factory pattern and factory method pattern. In the factory method pattern, a factory class is only responsible for producing a specific product, and a specific factory can produce a group of related products. Each of these three modes will be described in the following sections.

1. Simple factory mode

1.1 define

Define a class that creates objects that encapsulates the behavior (code) of instantiating objects

1.2 class diagram

The simple factory pattern puts the instantiated operations into a single class, which becomes the simple factory class, and lets the simple factory class decide which concrete subclasses should be instantiated.

1.3 the advantages

Doing so decouples the implementation of the client class from the concrete subclass; the client class no longer needs to know which subclasses and which should be instantiated. There are often multiple customer classes, and if you don’t use a simple factory, all customer classes know the details of all the subclasses, and if a subclass changes, such as adding a subclass, all customer classes are modified.

1.4 disadvantages

  1. The factory class centralizes the creation logic for all products and is so overloaded that if an exception occurs, the entire system will be affected.
  2. Using the simple factory pattern increases the number of classes in the system (introducing new factory classes), increasing the complexity and difficulty of understanding the system.
  3. It is difficult to expand the system. Once a new product is added, the factory logic has to be modified. The logic may be too complicated when there are many product types.

1.5 Application Scenarios

  1. The factory class is responsible for creating fewer objects because it does not overcomplicate the business logic in the factory methods.
  2. The client only knows the parameters passed into the factory class and does not care how the object is created.

1.6 Code Implementation

// Abstract the product interface
interface Product{}

// Specific product 1
class ConcreteProduct1 implements Product {
    constructor(){}
}

// Specific product 2
class ConcreteProduct2 implements Product {
    constructor(){}
}

// Simple factory
class SimpleFactory {
    public static createProduct(type : number) : Product {
        let product = null;
        if (type= = =1) {
            product = new ConcreteProduct1();
        } else if ( type= = =2) {
            product = new ConcreteProduct2();
        }

        returnproduct; }}/ / use
let product = SimpleFactory.createProduct(1);
Copy the code

2. Factory method pattern

2.1 define

Defines an abstract method for creating objects, with subclasses deciding which classes to instantiate. The factory method pattern postpones the instantiation of objects to subclasses.

2.2 class diagram

2.3 the advantages

  1. The user only needs to care about the factory of the product, not even the creation details or the class name of the specific product class.
  2. Can decide how to create which product objects, and the creation details are encapsulated inside the specific factory.
  3. When a new product is added to the system, there is no need to modify the abstract factory and the interface provided by the abstract product, no need to modify the client, and no need to modify other specific factories and products, just add a specific factory and specific products, so as to improve the scalability of the system (in accordance with the open and closed principle).

2.4 disadvantages

  1. Every time a product is added, a concrete class and object implementation factory need to be added, doubling the number of classes in the system, increasing the complexity of the system to a certain extent, but also increasing the dependence of the system concrete class.
  2. Considering the scalability of the system, it is necessary to introduce an abstraction layer, which is used to define the client code, increasing the abstraction and difficulty of understanding the system.

2.5 Application Scenarios

  1. You can use it wherever you need to generate objects (there is a tradeoff between adding a factory class because of the complexity of the code)
  2. Need a flexible, extensible framework
  3. Used in heterogeneous projects
  4. In a test-driven development architecture

2.6 Code Implementation

// Abstract the product interface
interface Product{
    method1() : void;
    method2() : void;
}

// Specific product 1
class ConcreteProduct1 implements Product {
    constructor(){}
    method1() {

    }
    method2() {

    }
}

// Specific product 2
class ConcreteProduct2 implements Product {
    constructor(){}
    method1() {

    }
    method2() {
        
    }
}

// Abstract factory
abstract class Creator {
    public abstract createProduct(type : number) : Product;
}

// Specific factory
class ConcreteCreator extends Creator {
    constructor() {super(a); }public createProduct(type : number) : Product {
        let product = null;
        if (type= = =1) {
            product = new ConcreteProduct1();
        } else if (type= = =2) {
            product = new ConcreteProduct2();
        }
        returnproduct; }}/ / use
const creator : Creator = new ConcreteCreator();
const myProduct : Product = creator.createProduct(1)
Copy the code

3. Abstract factory pattern

Let’s start with a diagram to understand two concepts: product family and product hierarchy.

Product family: a group of products produced by the same plant in a different product hierarchy. Adidas, for example, produces sneakers, canvas shoes, board shoes is a product family.

Product grade structure: refers to the inheritance structure of the product, for example, the board shoe column is a product grade structure.

3.1 define

Provides an interface for creating a set of related or interdependent objects without specifying their concrete classes.

3.2 class diagram

3.3 the advantages

  1. The generation of concrete classes is isolated so that clients do not need to know what is being created.
  2. When multiple objects from a product family are designed to work together, it ensures that clients always use only objects from the same product family.
  3. The abstract factory pattern makes it easy to add new product families without modifying existing systems, conforming to the open closed principle.

3.4 disadvantages

Adding a new product structure is cumbersome, requiring extensive changes to the existing system, and even modification of the abstraction layer code, which violates the open closed principle.

3.5 Application Scenarios

  1. When a system does not depend on the details of how instances of product classes are created, composed, and expressed, the user does not need to care about the object creation process, decoupling object creation and usage.
  2. There is more than one product family in the system, but only one product family needs to be used at a time. Product families can be dynamically changed through configuration files and new product families can be easily added.
  3. The product grade structure is stable. After the design is completed, no new product grade structure will be added to the system or the existing product grade structure will be deleted.

3.6 Code Implementation

// Abstract the factory interface
interface AbstractFactory {
    createProductA() : AbstractProductA;
    createProductB() : AbstractProductB;
}

// Abstract product A interface
interface AbstractProductA {}

// Abstract product B interface
interface AbstractProductB {}

// Factory 1
class ConcreteFactory1 implements AbstractFactory {
    constructor() {}
    public createProductA() : AbstractProductA {
        return new ConcreteProductA1();
    }
    public createProductB() : AbstractProductB {
        return newConcreteProductB1(); }}// Factory 2
class ConcreteFactory2 implements AbstractFactory {
    constructor() {}
    public createProductA() : AbstractProductA {
        return new ConcreteProductA2();
    }
    public createProductB() : AbstractProductB {
        return newConcreteProductB2(); }}// Product A1
class ConcreteProductA1 implements AbstractProductA {}
// Specific product A2
class ConcreteProductA2 implements AbstractProductA {}
// Product B1
class ConcreteProductB1 implements AbstractProductB {}
// Specific product B2
class ConcreteProductB2 implements AbstractProductA {}

/ / use
const factory1 : AbstractFactory = new ConcreteFactory1();
const factory2 : AbstractFactory = new ConcreteFactory2();
const productA1 : AbstractProductA = factory1.createProductA();
const productA2 : AbstractProductA = factory2.createProductA();
const productB1 : AbstractProductB = factory1.createProductB();
const productB2 : AbstractProductB = factory2.createProductB();
Copy the code