Technology Sharing

Design Patterns: Factory Pattern

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Factory Pattern is a commonly used design pattern in object-oriented programming.Creational PatternsThe factory pattern is mainly used to create objects, but it is different from using it directly in the code.newUnlike creating objects with keywords, it creates objects through a common interface, thus separating the object creation process from the client code. The following is a detailed introduction to the factory pattern:

1. Main features of the factory model

  1. Encapsulates the object creation process: The object creation process is encapsulated in the factory class. The client only needs to specify the type of object to be created, and then it can obtain the required object through the factory class without having to worry about the specific creation details of the object.
  2. Reduce coupling: Through the isolation of abstract factories and concrete factories, the coupling between clients and objects is reduced, making client programs easier to expand and maintain.
  3. Improve the scalability of the program: When a new product needs to be added, you only need to add the corresponding specific product class and the corresponding factory class without modifying the original code. This complies with the open-closed principle (open for extension and closed for modification), and improves the scalability of the code.
  4. Easy to maintain and upgrade: Because the factory method concentrates the process of creating objects in one class, if you need to modify the method of creating objects, you only need to modify the corresponding factory class, which has no impact on the client.
  5. Code Reuse: The factory pattern can abstract the object creation process, realize code reuse, and increase the flexibility and reusability of the code.

2. Classification of factory patterns

The factory pattern is mainly divided into three types: simple factory pattern (static factory pattern), factory method pattern and abstract factory pattern

1. Static Factory Method
  1. Define a factory class that returns instances of different classes based on the different parameters passed in.
  2. A factory class usually contains multiple static methods, each of which corresponds to the creation of a specific product.
  3. Advantages: Simple to implement and easy to understand.
  4. Disadvantages: The factory class has too many responsibilities. When adding new products, the source code of the factory class needs to be modified, which violates the open-closed principle.

Simple factory pattern class diagram

2. Factory Method
  1. Define an interface for creating objects (abstract factory),But let the subclass decide which class to instantiate
  2. Factory methods let you defer instantiation of a class to subclasses.
  3. Advantages: Good scalability. When you need to add new products, you only need to add the corresponding specific product classes and specific factory classes.
  4. Disadvantages: Every time a product is added, a specific class and object implementation factory need to be added, which increases the number of classes in the system exponentially and increases the complexity of the system.

Factory Method Pattern

3. Abstract Factory
  1. Provides an interface for creating a set of related or interdependent objects without specifying their concrete classes.
  2. The abstract factory pattern can be seen as the combined use of multiple factory method patterns.
  3. Advantages: Product families can be constrained within the class, and a specific factory must be able to create all products in the product family.
  4. Disadvantages: It increases the abstractness and difficulty of understanding of the system, and the number of classes in the system will increase significantly.

Abstract Factory Pattern

3. Application scenarios of factory mode

The factory pattern is widely used in actual development, such as database connection, UI controls, file processing, logging, network communication, message queues, data structures, encryption and decryption, message push, and task scheduling. In these scenarios, the factory pattern can be used to separate the object creation process from the use process, thereby improving the flexibility and scalability of the system.

4. Factory Pattern Example

1. Simple factory pattern example

In the simple factory pattern, we define a factory class that can return instances of different types based on the parameters passed in.

Below is an example of a simple factory pattern written in Java. We will create a shape factory that can produce instances of circle, rectangle, and square. First, we define a shape interface (Shape) and several concrete classes that implement the interface (Circle, Rectangle, Square).

  1. // 形状接口
  2. interface Shape {
  3. void draw();
  4. }
  5. // 圆形类
  6. class Circle implements Shape {
  7. @Override
  8. public void draw() {
  9. System.out.println("Inside Circle::draw() method.");
  10. }
  11. }
  12. // 矩形类
  13. class Rectangle implements Shape {
  14. @Override
  15. public void draw() {
  16. System.out.println("Inside Rectangle::draw() method.");
  17. }
  18. }
  19. // 正方形类
  20. class Square implements Shape {
  21. @Override
  22. public void draw() {
  23. System.out.println("Inside Square::draw() method.");
  24. }
  25. }

Next, we define a factory class (ShapeFactory) that uses static methods to generate shape objects.

  1. // 形状工厂类
  2. class ShapeFactory {
  3. // 使用 getShape 方法获取形状类型的对象
  4. public static Shape getShape(String shapeType){
  5. if(shapeType == null){
  6. return null;
  7. }
  8. if(shapeType.equalsIgnoreCase("CIRCLE")){
  9. return new Circle();
  10. } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
  11. return new Rectangle();
  12. } else if(shapeType.equalsIgnoreCase("SQUARE")){
  13. return new Square();
  14. }
  15. return null;
  16. }
  17. }

Finally, we can use the factory class to get shape objects and call theirdrawmethod.

  1. public class FactoryPatternDemo {
  2. public static void main(String[] args) {
  3. // 获取 Circle 的对象,并调用它的 draw 方法
  4. Shape shape1 = ShapeFactory.getShape("CIRCLE");
  5. shape1.draw();
  6. // 获取 Rectangle 的对象,并调用它的 draw 方法
  7. Shape shape2 = ShapeFactory.getShape("RECTANGLE");
  8. shape2.draw();
  9. // 获取 Square 的对象,并调用它的 draw 方法
  10. Shape shape3 = ShapeFactory.getShape("SQUARE");
  11. shape3.draw();
  12. }
  13. }

In this example,ShapeFactoryThe class is responsible for creatingShapeThe client code passes type information (such as a string) toShapeFactoryStatic methods of a classgetShapeto get an instance of the required shape. In this way, the client is decoupled from the implementation of the concrete class, which complies with the open-closed principle, that is, the new shape class can be extended without modifying the client code. However, a major disadvantage of the simple factory pattern is that if the system needs to expand a new product family (for example, in addition to the shape, there are also colors, etc.), the factory class needs to be modified, which violates part of the open-closed principle. In this case, you can consider using the factory method pattern or the abstract factory pattern.

2. Factory Method Pattern Example

Let’s take shape as an example. We use engineering methods to create it.

First, define the shape interface (Product) and create three implementation classes.

  1. // 形状接口
  2. interface Shape {
  3. void draw();
  4. }
  5. // 圆形类
  6. class Circle implements Shape {
  7. @Override
  8. public void draw() {
  9. System.out.println("Inside Circle::draw() method.");
  10. }
  11. }
  12. // 矩形类
  13. class Rectangle implements Shape {
  14. @Override
  15. public void draw() {
  16. System.out.println("Inside Rectangle::draw() method.");
  17. }
  18. }
  19. // 正方形类
  20. class Square implements Shape {
  21. @Override
  22. public void draw() {
  23. System.out.println("Inside Square::draw() method.");
  24. }
  25. }

Next, define the creator interface (Creator) and the concrete creator (Concrete Creator):

  1. // 创建者接口
  2. interface ShapeFactory {
  3. Shape getShape(String shapeType);
  4. }
  5. // 具体创建者类
  6. class RectangleFactory implements ShapeFactory {
  7. @Override
  8. public Shape getShape(String shapeType) {
  9. if (shapeType == null) {
  10. return null;
  11. }
  12. if (shapeType.equalsIgnoreCase("RECTANGLE")) {
  13. return new Rectangle();
  14. }
  15. return null;
  16. }
  17. }
  18. class CircleFactory implements ShapeFactory {
  19. @Override
  20. public Shape getShape(String shapeType) {
  21. if (shapeType == null) {
  22. return null;
  23. }
  24. if (shapeType.equalsIgnoreCase("CIRCLE")) {
  25. return new Circle();
  26. }
  27. return null;
  28. }
  29. }
  30. class SquareFactory implements ShapeFactory {
  31. @Override
  32. public Shape getShape(String shapeType) {
  33. if (shapeType == null) {
  34. return null;
  35. }
  36. if (shapeType.equalsIgnoreCase("SQUARE")) {
  37. return new Square();
  38. }
  39. return null;
  40. }
  41. }

Finally, use the concrete creator class to get the shape objects and call theirdrawmethod:

  1. public class FactoryMethodPatternDemo {
  2. public static void main(String[] args) {
  3. ShapeFactory shapeFactory = new CircleFactory();
  4. // 获取 Circle 的对象,并调用它的 draw 方法
  5. Shape shape1 = shapeFactory.getShape("CIRCLE");
  6. shape1.draw();
  7. // 使用 RectangleFactory 来获取 Rectangle 的对象
  8. shapeFactory = new RectangleFactory();
  9. Shape shape2 = shapeFactory.getShape("RECTANGLE");
  10. shape2.draw();
  11. // 使用 SquareFactory 来获取 Square 的对象
  12. shapeFactory = new SquareFactory();
  13. Shape shape3 = shapeFactory.getShape("SQUARE");
  14. shape3.draw();
  15. }
  16. }

In this example,We create a corresponding factory class for each shape class(Although in real applications, you would usually have a more general factory class structure, perhaps using configuration files or reflection to avoid having to write a separate factory class for each product class.) This helps demonstrate the basic principles of the Factory Method pattern,That is, let the factory subclass decide which class to instantiateHowever, for the simple case in this example, you might prefer to use the Simple Factory pattern or consider other design patterns to reduce code duplication.

3. Abstract Factory Example

This example takes mobile phones as an example, abstract products related to mobile phones (such as screens, batteries, operating systems, etc.), and creates specific product classes to implement these interfaces. Then, we can define abstract factory interfaces and specific factory classes to produce these products. The following is a simplified example showing how to use the abstract factory pattern to produce mobile phone components (screens and batteries) of different brands and models:

First, define the product interface and specific products:

  1. // 屏幕接口
  2. interface Screen {
  3. void display();
  4. }
  5. // 高端屏幕的实现
  6. class HighEndScreen implements Screen {
  7. @Override
  8. public void display() {
  9. System.out.println("显示高清屏幕");
  10. }
  11. }
  12. // 低端屏幕的实现
  13. class LowEndScreen implements Screen {
  14. @Override
  15. public void display() {
  16. System.out.println("显示普通屏幕");
  17. }
  18. }
  19. // 电池接口
  20. interface Battery {
  21. void charge();
  22. }
  23. // 高效电池的实现
  24. class HighEfficiencyBattery implements Battery {
  25. @Override
  26. public void charge() {
  27. System.out.println("快速充电");
  28. }
  29. }
  30. // 标准电池的实现
  31. class StandardBattery implements Battery {
  32. @Override
  33. public void charge() {
  34. System.out.println("标准充电");
  35. }
  36. }

Next, define the abstract factory interface and concrete factory class:

  1. // 手机组件工厂接口
  2. interface PhoneFactory {
  3. Screen createScreen();
  4. Battery createBattery();
  5. }
  6. // 高端手机工厂
  7. class HighEndPhoneFactory implements PhoneFactory {
  8. @Override
  9. public Screen createScreen() {
  10. return new HighEndScreen();
  11. }
  12. @Override
  13. public Battery createBattery() {
  14. return new HighEfficiencyBattery();
  15. }
  16. }
  17. // 低端手机工厂
  18. class LowEndPhoneFactory implements PhoneFactory {
  19. @Override
  20. public Screen createScreen() {
  21. return new LowEndScreen();
  22. }
  23. @Override
  24. public Battery createBattery() {
  25. return new StandardBattery();
  26. }
  27. }

Finally, the client code, which uses the abstract factory to create concrete product objects:

  1. public class PhoneFactoryPatternDemo {
  2. public static void main(String[] args) {
  3. // 使用高端手机工厂
  4. PhoneFactory highEndFactory = new HighEndPhoneFactory();
  5. Screen highEndScreen = highEndFactory.createScreen();
  6. Battery highEndBattery = highEndFactory.createBattery();
  7. highEndScreen.display();
  8. highEndBattery.charge();
  9. // 使用低端手机工厂
  10. PhoneFactory lowEndFactory = new LowEndPhoneFactory();
  11. Screen lowEndScreen = lowEndFactory.createScreen();
  12. Battery lowEndBattery = lowEndFactory.createBattery();
  13. lowEndScreen.display();
  14. lowEndBattery.charge();
  15. }
  16. }

In this example, we define two mobile phone factories (high-end and low-end), each capable of producing a specific type of screen and battery.The client code interacts with the specific factory class through the abstract factory interface, thereby achieving decoupling from the specific product class.In this way, if we need to add new types of mobile phones in the future (for example, mid-range mobile phones), we only need to add new product classes and corresponding factory classes without modifying existing client code.

V. Conclusion

The factory pattern is a very useful design pattern.It has the advantages of encapsulating the object creation process, reducing coupling, improving the scalability and maintainability of the program, etc., which has been widely used in software development. However, it also has someDisadvantages, such as excessive factory responsibilities and difficulty in system expansion, etc.Therefore, in practical applications, it is necessary to make trade-offs and choices based on specific scenarios and requirements.

If the factory pattern (simple factory, factory method, abstract factory) is useful to you, remember to like and collect it.