Creational Design Pattern 05-Abstract Factory Pattern

Creational Design Pattern 05-Abstract Factory Pattern

1. Introduction to the abstract factory pattern

Abstract Factory Pattern is a creational design pattern that creates other factories around a super factory, also called a factory (a small factory in a big factory). In the abstract factory pattern, an interface is responsible for creating a factory for a set of related objects without explicitly specifying their classes. This design pattern can separate the client from the specific implementation, allowing the client to create multiple related product objects without having to know the specific product.

1.1 Structure diagram of abstract factory

img

Abstract Factory pattern (Abstract Factory) provides an interface for creating a series of related or interdependent objects without specifying their specific classes.

AbstractFactory: Abstract factory class defines a set of abstract methods for creating products (ProductA and ProductB). The specific factory class will implement these abstract methods.

ConcreteFactory1 and ConcreteFactory2: Concrete factory classes that implement the abstract methods for creating products defined in AbstractFactory. In this example, ConcreteFactory1 is used to create instances of ProductA1 and ProductB1, while ConcreteFactory2 is used to create instances of ProductA2 and ProductB2.

AbstractProductA and AbstractProductB: abstract product classes, which define the general interface of the product. Specific product classes will implement these interfaces.

ProductA1, ProductA2, ProductB1 and ProductB2: specific product classes that implement the interfaces defined in AbstractProductA and AbstractProductB.

When a client needs to create a product, it first creates a concrete factory class and then uses that factory class to create the required product instance. Because the abstract factory provides a set of related product interfaces, these products have something in common and can be easily combined, paired, or exchanged. The specific factory dynamically creates product instances at runtime according to the client's needs, thereby achieving decoupling between the client and the specific implementation.

Let’s take a look at the specific implementation of this.

1.2 Abstract factory template implementation

Abstract product category:

// 抽象产品类 A
interface AbstractProductA {
  void operationA();
}

// 抽象产品类 B
interface AbstractProductB {
  void operationB();
}

Implement different abstract products and produce different specific products:

// 具体产品类 A1
class ProductA1 implements AbstractProductA {
  @Override
  public void operationA() {
    System.out.println("具体产品类 A1 实现抽象产品 A");
  }
}

// 具体产品类 A2
class ProductA2 implements AbstractProductA {
  @Override
  public void operationA() {
    System.out.println("具体产品类 A2 实现抽象产品 A");
  }
}

// 具体产品类 B1
class ProductB1 implements AbstractProductB {
  @Override
  public void operationB() {
    System.out.println("具体产品类 B1 实现抽象产品 B");
  }
}

// 具体产品类 B2
class ProductB2 implements AbstractProductB {
  @Override
  public void operationB() {
    System.out.println("具体产品类 B2 实现抽象产品 B");
  }
}

Abstract factory class: class for creating products

It is equivalent to a large factory, which can produce many products, but each product belongs to a small factory within the large factory. That is, there are small factories within the large factory, and specific products are implemented in the small factory.

// 抽象工厂类
interface AbstractFactory {
  AbstractProductA createProductA();
  AbstractProductB createProductB();
}

Specific small factories:

// 具体工厂类 1
class ConcreteFactory1 implements AbstractFactory {
  @Override
  public AbstractProductA createProductA() {
    return new ProductA1();
  }

  @Override
  public AbstractProductB createProductB() {
    return new ProductB1();
  }
}

// 具体工厂类 2
class ConcreteFactory2 implements AbstractFactory {
  @Override
  public AbstractProductA createProductA() {
    return new ProductA2();
  }

  @Override
  public AbstractProductB createProductB() {
    return new ProductB2();
  }
}

Client implementation:

// 客户端
public class Client {
  public static void main(String[] args) {
    // 创建具体工厂类 1
    AbstractFactory factory1 = new ConcreteFactory1();

    // 使用工厂类 1 创建产品 A 和 B
    AbstractProductA productA1 = factory1.createProductA();
    AbstractProductB productB1 = factory1.createProductB();

    // 调用产品 A 和 B 的方法
    productA1.operationA();
    productB1.operationB();

    // 创建具体工厂类 2
    AbstractFactory factory2 = new ConcreteFactory2();

    // 使用工厂类 2 创建产品 A 和 B
    AbstractProductA productA2 = factory2.createProductA();
    AbstractProductB productB2 = factory2.createProductB();

    // 调用产品 A 和 B 的方法
    productA2.operationA();
    productB2.operationB();
  }
}

2. Implementation of specific examples

Let's say you are an interior designer and you need to design furniture for a client's room. Different customers have different styles and budgets, so you need to choose different furniture manufacturers to meet their needs. In this case, you can apply the abstract factory pattern to furniture manufacturing.

First, you need to define a furniture abstract factory interface, which has two methods: createChair() and createSofa(), which are used to create chairs and sofas respectively. Then, you can create multiple concrete furniture factory classes (for example, modern furniture factory, traditional furniture factory, etc.), which respectively implement the furniture abstract factory interface and are responsible for creating different types of furniture.

Finally, you can choose the corresponding furniture factory to create furniture according to customer needs. For example, if a customer needs modern furniture, you can use a modern furniture factory to create modern-style chairs and sofas; if a customer needs traditional furniture, you can use a traditional furniture factory to create traditional-style chairs and sofas.

In this way, the abstract factory pattern helps you decouple customers from specific furniture manufacturers, making it easier for you to switch between different manufacturers, while also providing customers with more choices.

2.1 Implementation without using abstract factory pattern

Furniture category:

/**
 * @author Shier
 * 家具类
 */
public class Furniture {
    private String type;
    private String style;
    private double price;

    public Furniture(String type, String style, double price) {
        this.type = type;
        this.style = style;
        this.price = price;
    }
    
    public void printInfo() {
        System.out.println("Type: " + type);
        System.out.println("Style: " + style);
        System.out.println("Price: " + price);
    }
}

Modern style category:

/**
 * @author Shier
 * 现代风格家具制造商
 */
public class ModernFurnitureMaker {
    public Furniture createChair() {
        return new Furniture("椅子", "现代风格", 150.0);
    }

    public Furniture createSofa() {
        return new Furniture("沙发", "现代风格", 500.0);
    }
}

Traditional style category:

/**
 * @author Shier
 * 传统风格家具制造商
 */
public class TraditionalFurnitureMaker {
    public Furniture createChair() {
        return new Furniture("椅子", "传统风格", 100.0);
    }

    public Furniture createSofa() {
        return new Furniture("沙发", "传统风格", 600.0);
    }
}

Client class:

/**
 * @author Shier
 * 客户端类
 */
public class Client {
    public static void main(String[] args) {
        // 创建现代风格家具
        ModernFurnitureMaker modernMaker = new ModernFurnitureMaker();
        Furniture modernChair = modernMaker.createChair();
        Furniture modernSofa = modernMaker.createSofa();

        // 打印信息
        modernChair.printInfo();
        modernSofa.printInfo();

        // 创建传统风格家具
        TraditionalFurnitureMaker traditionalMaker = new TraditionalFurnitureMaker();
        Furniture traditionalChair = traditionalMaker.createChair();
        Furniture traditionalSofa = traditionalMaker.createSofa();

        // 打印信息
        traditionalChair.printInfo();
        traditionalSofa.printInfo();
    }
}

In this example, we create two classes to represent modern and traditional furniture manufacturers. Each furniture manufacturer has its own createChair() and createSofa() methods to create corresponding types of furniture objects.

In the client code, we create concrete furniture maker objects (i.e. ModernFurnitureMaker and TraditionalFurnitureMaker) and use them to create furniture objects. Then we printed the information for each furniture object.

Although this example does not use the abstract factory pattern, it still meets the basic needs. But as more homes need to be made, more and more duplicate codes will appear.

Disadvantages of not using the Abstract Factory pattern may include:

Not easy to extend: If you want to add a new product type, you need to modify all the code that creates the product. This modification may involve multiple classes and is not easy to maintain.

Binding to a specific product implementation: The client directly relies on the specific product implementation, rather than an interface or abstract class. This makes the client code and the specific product implementation tightly bound together, making it difficult to replace or test. (High coupling)

May lead to code duplication: If multiple clients need to create the same type of product, the same creation code needs to be copied in each client. This duplication of code can increase maintenance costs and be prone to errors.

2.2 Implementation using abstract factory pattern

Abstract product category:

/**
 * 抽象产品类-椅子
 */
public interface Chair {
    void printInfo();
}
/**
 * 抽象产品类-沙发
 */
public interface Sofa {
  void printInfo();
}

Specific product categories:

/**
 * @author Shier
 * 具体产品类-现代椅子
 */
public class ModernChair implements Chair {
    public void printInfo() {
        System.out.println("类型: 椅子, 风格: 现代, 价格: 150.0");
    }
}
/**
 * @author Shier
 * 具体产品类-现代沙发
 */
public class ModernSofa implements Sofa {
  public void printInfo() {
    System.out.println("类型: 沙发, 风格: 现代, 价格: 500.0");
  }
}
/**
 * @author Shier
 * 具体产品类-传统椅子
 */
public class TraditionalChair implements Chair {
    public void printInfo() {
        System.out.println("类型: 椅子, 风格: 传统, 价格: 100.0");

    }
}
/**
 * @author Shier
 * 具体产品类-传统沙发
 */
public class TraditionalSofa implements Sofa {
  public void printInfo() {
    System.out.println("类型: 沙发, 风格: 传统, 价格: 600.0");
  }
}

Specific product factory categories:

/**
 * @author Shier
 * 具体工厂类-现代风格家具
 */
public class ModernFurnitureFactory implements FurnitureMaker {
    public Chair createChair() {
        return new ModernChair();
    }

    public Sofa createSofa() {
        return new ModernSofa();
    }
}
/**
 * @author Shier
 * 具体工厂类-传统风格家具
 */
public class TraditionalFurnitureFactory implements FurnitureMaker {
  public Chair createChair() {
    return new TraditionalChair();
  }

  public Sofa createSofa() {
    return new TraditionalSofa();
  }
}

Client class:

/**
 * @author Shier
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 创建现代风格家具工厂
        FurnitureMaker modernFactory = new ModernFurnitureFactory();
        Chair modernChair = modernFactory.createChair();
        Sofa modernSofa = modernFactory.createSofa();
        // 打印信息
        modernChair.printInfo();
        modernSofa.printInfo();
        // 创建传统风格家具工厂
        FurnitureMaker traditionalFactory = new TraditionalFurnitureFactory();
        Chair traditionalChair = traditionalFactory.createChair();
        Sofa traditionalSofa = traditionalFactory.createSofa();
        // 打印信息
        traditionalChair.printInfo();
        traditionalSofa.printInfo();
    }
}

We first created two abstract product classes, Chair and Sofa, to represent different types of furniture. Then, we created each specific product class to implement the corresponding product.

Next, we created the FurnitureMaker abstract factory class, which contains two abstract methods createChair() and createSofa(). Each concrete factory class needs to implement these methods to create furniture objects of the corresponding type.

In the client code, we create concrete furniture manufacturer objects (i.e. ModernFurnitureFactory and TraditionalFurnitureFactory) and use them to create furniture objects. Then we printed the information for each furniture object.

Although it seems that more classes are created than without using the abstract factory method, by using the abstract factory pattern, we can separate the creation of products from the specific product implementation, and can easily add new product types or modify existing products Implemented without modifying client code.

3. Summary of abstract factory pattern

Advantages of the abstract factory pattern:

It can ensure that the client uses the same type of product object, thus avoiding compatibility issues between different products.

It only needs to appear once in an application during initialization, which makes it very easy to change the specific factory of an application. It only needs to change the specific factory to use different product configurations.

The implementation of specific products is hidden, thereby separating client code from specific product implementations, enhancing scalability and flexibility. Let the specific instance creation process be separated from the client. The client manipulates the instance through their abstract interface. The specific class name of the product is also separated by the implementation of the specific factory and will not appear in the client code.

The system is easier to extend because it is easy to add new specific plants and product families without modifying the original code.

Disadvantages of the abstract factory pattern:

When a new product family needs to be added, the interface of the abstract factory needs to be modified, which will cause all concrete factories to need to be modified and the system will become unstable.

When a new product object needs to be added, in addition to adding a new concrete product class, the abstract factory interface and all concrete factory classes also need to be modified, which will lead to an increase in the maintenance cost of the system.

While increasing the abstraction layer, it also increases the complexity and difficulty of understanding the system.

The abstract factory pattern is suitable for the following situations:

A system requires a set of related or interdependent objects, often with a common interface.

The system does not care about the details of how a specific product is created or implemented, but only cares about the specifications and functions of the product.

There are multiple product families in the system, and each product family has some common constraints. These constraints are not realized through class inheritance relationships.

Common application scenarios include:

User interface toolkit: For example, if you need to create interface components such as buttons and text boxes in different styles (such as Windows, Mac OS, Linux, etc.), you can use the abstract factory pattern to create a factory for each style. The factory can create the required All components.

Data access library: For example, if you need to provide data access objects for different databases such as Oracle, MySQL, SQL Server, etc., you can use the abstract factory pattern to create a factory for each database. The factory can create the required connections, commands and other objects. (This example is used to illustrate the abstract factory pattern in "Dahua Design Patterns")

Medical equipment control system: For example, you need to control different types of medical equipment (such as electrocardiographs, blood pressure monitors, drug pumps, etc.). Each type of equipment has its own control protocol. You can use the abstract factory pattern to create a Factory, the factory can create objects that control the device of this model.

The system does not care about the details of how a specific product is created or implemented, but only cares about the specifications and functions of the product.

There are multiple product families in the system, and each product family has some common constraints. These constraints are not realized through class inheritance relationships.

Common application scenarios include:

User interface toolkit: For example, if you need to create interface components such as buttons and text boxes in different styles (such as Windows, Mac OS, Linux, etc.), you can use the abstract factory pattern to create a factory for each style. The factory can create the required All components.

Data access library: For example, if you need to provide data access objects for different databases such as Oracle, MySQL, SQL Server, etc., you can use the abstract factory pattern to create a factory for each database. The factory can create the required connections, commands and other objects. (This example is used to illustrate the abstract factory pattern in "Dahua Design Patterns")

Medical equipment control system: For example, you need to control different types of medical equipment (such as electrocardiographs, blood pressure monitors, drug pumps, etc.). Each type of equipment has its own control protocol. You can use the abstract factory pattern to create a Factory, the factory can create objects that control the device of this model.

This article is published by OpenWrite, a blog that publishes multiple articles !

OpenAI opens ChatGPT to all users for free. Voice programmers tampered with ETC balances and embezzled more than 2.6 million yuan a year. Spring Boot 3.2.0 was officially released. Google employees criticized the big boss after leaving the company. He was deeply involved in the Flutter project and formulated HTML-related standards. Microsoft Copilot Web AI will be Officially launched on December 1st, supporting Chinese Microsoft's open source Terminal Chat Rust Web framework Rocket releases v0.5: supports asynchronous, SSE, WebSockets, etc. The father of Redis implements the Telegram Bot framework using pure C language code . If you are an open source project maintainer, encounter How far can you endure this kind of response? PHP 8.3 GA
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/5587102/blog/10142075