Tell a story and see if you can understand the factory method pattern

Factory method pattern

Foreword

Today I want to talk about a common pattern, the factory method pattern. In addition, there will be a lot of code in this article, but it is very simple and easy to understand.

The following two pictures may be used in this article. Those who are interested can take a look. I hope you can read this article, if you like it, you can give it a thumbs up and support it.

Common design principles

Design principle.png

Types of factory patterns

factory mode.png

Tell a story

In recent years, new energy vehicles have become more popular. Tesla, BYD, Wuling and other car companies are all developing new energy vehicles. As for cars, the brands are different, the configurations are different, and the prices are not the same. Xiaobai, a graduate, works for an automotive media company. One day, the team leader called him and asked him to collect information about the new energy vehicles of these auto companies. Today’s story begins here.

Normal writing

After receiving the task, Xiaobai thought to himself, this task is too simple, without any difficulty. So he started working immediately, so he had the following code.

Product categories

// BYD class
class BYD{<!-- -->
    public void name() {<!-- -->
        System.out.println("BYD");
    }
    public void price() {<!-- -->
        System.out.println("220,000");
    }
    public void kind() {<!-- -->
        System.out.println("new energy");
    }
}
// Tesla class
class TSLA{<!-- -->
    public void name() {<!-- -->
        System.out.println("Tesla");
    }

    public void price() {<!-- -->
        System.out.println("320,000");
    }

    public void kind() {<!-- -->
        System.out.println("New Energy Vehicle");
    }
}

Client class

// client call code
class Client {<!-- -->
    public static void main(String[] args) {<!-- -->
        BYD byd = new BYD();
        TSLA tsla = new TSLA();
    }
}

It looks pretty good so far, and both brands have their own targets. So he immediately handed the code to the team leader.

Problems encountered in ordinary writing

After looking at it, the team leader said to him: For now, it’s okay to only have two brands, but the business will definitely need to be developed in the future, and there must be more than one brand.
What should I do if there are 10 car brands and 100 car brands in the Client? Should a Client class be associated with 100 car brand classes? Dimit’s law – the principle of least knowledge, haven’t you heard of it?
Once one of the 100 classes is modified, it can cause problems with the Client class!
You go to compare the design principles and see where the problem lies! ! !

Writing method of simple factory pattern

Xiaobai, after reading the code carefully, I found that the current way of writing is indeed too serious for coupling. The Client class will be associated with each car product class used.
Fortunately, we can use Dependency Inversion + Encapsulation to modify the original code to solve this problem.

  1. Extract the attributes or methods that all cars have into the car interface
  2. All automotive products implement the automotive interface. In this way, the client class can be decoupled from the car product through the polymorphic feature.
  3. Encapsulate the creation process of all objects into a management class, which can be called Manager or Factory.

Code of Simple Factory Pattern

1. Extract the public method into the interface
/**
 * @author jtl
 * @date 2021/7/20 16:18
 */
interface Car {<!-- -->
    void name();

    void price();

    void kind();
}
2. The car brand implements this interface
class BYD implements Car {<!-- -->
    @Override
    public void name() {<!-- -->
        System.out.println("BYD");
    }

    @Override
    public void price() {<!-- -->
        System.out.println("220,000");
    }

    @Override
    public void kind() {<!-- -->
        System.out.println("new energy");
    }
}

class BMW implements Car{<!-- -->
    @Override
    public void name() {<!-- -->
        System.out.println("BMW");
    }

    @Override
    public void price() {<!-- -->
        System.out.println("400,000");
    }

    @Override
    public void kind() {<!-- -->
        System.out.println("fuel vehicle");
    }
}

class Tesla implements Car {<!-- -->
    @Override
    public void name() {<!-- -->
        System.out.println("Tesla");
    }

    @Override
    public void price() {<!-- -->
        System.out.println("320,000");
    }

    @Override
    public void kind() {<!-- -->
        System.out.println("New Energy Vehicle");
    }
}

3. Car management factory class
class CarFactory {<!-- -->

    public static Car getCar(String name) {<!-- -->
        Car car = null ;
        switch (name){<!-- -->
            case "BMW":
                car = new BMW();
                break;
            case "Tesla":
                car = new TSLA();
                break;
            case "BYD":
                car = new BYD();
                break;
            default:
                break;
        }

        return car;
    }
}
4. Client call
class Client {<!-- -->
    public static void main(String[] args) {<!-- -->
        Car car = CarFactory.getCar("BYD");
        Car car = CarFactory.getCar("Tesla");
        Car car = CarFactory.getCar("BMW");
    }
}

Problems encountered by Simple Factory

Through the Simple Factory Pattern, the Client class only deals with Car and CarFactory, which reduces the coupling. Even if there are 1000 car brands, they have nothing to do with the Client class. .

But with that came a new problem. Every time you want to add a new car brand, you must modify the methods in the Factory class. This violates one of the most critical design principles, the Open-Closed Principle, that is, open for extension but closed for modification.

And although the coupling of the Client class has come down, all the car classes are associated with the Factory class. This still violates Dimeter’s Law – Principle of Least Knowing.

Factory method pattern

When Xiaobai was at a loss, the team leader came over and said to him. You can think of this step is already very good, I have a code here that has been uploaded to GitLab, which uses the factory method pattern. Pull it out to see how it’s written. Then without waiting for Xiaobai to say thank you, he turned and left. After Xiaobai pulled the code down, he saw the following code.

Factory methods implemented in the normal way

Abstract product code
/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:17
 * Detail: abstract product interface
 */
public interface ICar {<!-- -->
    void name();

    void price();
}
Actual product code
/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:20
 * Detail (details): Specific product categories - BYD electric vehicles
 */
public class BYD implements ICar{<!-- -->
    @Override
    public void name() {<!-- -->
        System.out.println("BYD New Energy Electric Vehicle");
    }

    @Override
    public void price() {<!-- -->
        System.out.println("20W");
    }
}

/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:21
 * Detail: Specific product category---Tesla Electric Vehicle
 */
public class TSLA implements ICar{<!-- -->
    @Override
    public void name() {<!-- -->
        System.out.println("Tesla New Energy Electric Vehicle");
    }

    @Override
    public void price() {<!-- -->
        System.out.println("28W");
    }
}
Abstract factory code
/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:18
 * Detail: abstract factory interface
 */
public interface IFactory {<!-- -->
    ICar buildCar();
}
Actual factory code
/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:23
 * Detail (details): Specific factory category --- BYD new energy factory
 */
public class BYDFactory implements IFactory{<!-- -->
    @Override
    public ICar buildCar() {<!-- -->
        return new BYD();
    }
}

/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:24
 * Detail (details): specific factory category---Tesla Shanghai Factory
 */
public class TSLAFactory implements IFactory{<!-- -->
    @Override
    public ICar buildCar() {<!-- -->
        return new TSLA();
    }
}
Client call code
/**
 * Author (Author): jtl
 * Date (date): 2023/2/8 14:24
 * Detail: factory method pattern client
 */
public class Client {<!-- -->
    public static void main(String[] args) {<!-- -->
        IFactory bydFactory = new BYDFactory();
        bydFactory.buildCar().name();

        IFactory tslaFactory = new TSLAFactory();
        tslaFactory.buildCar().name();
    }
}

To solve, the Simple Factory Pattern violates the Open-Closed Principle and Demeter’s Law – Least Known Principle. The team leader made modifications to Simple Factory.

  1. Extract properties or methods that are shared by cars into **car interface (ICar)**
  2. All Car products (TSLA, BYD, etc.) implement the Car Interface (ICar). In this way, the decoupling between the Client class and the car product can be carried out through the feature of polymorphism.
  3. Extract and encapsulate the function of creating automotive products TSLA, BYD, etc.) into IFactory factory interface
  4. Each automobile product (TSLA, BYD, etc.) has its own automobile factory (TSLAFactory, BYDFactory, etc.) that implements the IFactory interface

New question

Although, the Factory Method Pattern solves the problem of too high coupling between the Factory class and various car product classes in the Simple Factory. And it is no longer necessary to go to the simple factory to modify the code every time a new car product is added.

But a fatal problem with the factory method model is that every time a new car brand is added, a car product and car factory must be added. Add a Wuling car and Wuling factory today, and add an ideal car and an ideal factory tomorrow. Adding 1,000 new car brands requires adding 1,000 new cars and factories. In this way, the number of class will be too much. This is a problem that cannot be ignored.

Factory methods implemented by reflection

Xiao Bai told the team leader exactly what he thought of. The team leader looked at Xiaobai very comfortingly and said, it is good that you can think of this question, which means you have made progress. The factory method pattern does have such a problem. So I used reflection to write a code for the factory method pattern. Although it has nothing to do with cars, you can make an analogy. After all, everything cannot be related to the car!

Abstract factory code

Corresponding IFactory

public abstract class Factory {<!-- -->
   public abstract <T extends Product> T createProduct(Class<T> clazz);
}
Abstract product code

Corresponding ICar

public abstract class Product {<!-- -->
    abstract void method();
}
Specific factory code

Corresponding to BYDFactory and other automobile factories

public class ConcreteFactory extends Factory{<!-- -->

    // Create a specific factory through reflection, which solves the problem that a specific factory corresponds to a specific product
    @Override
    public <T extends Product> T createProduct(Class<T> clazz) {<!-- -->
        Product product = null;
        try {<!-- -->
            product = (Product) Class.forName(clazz.getName()).newInstance();
        }catch (Exception e){<!-- -->
            e.printStackTrace();
        }

        return (T) product;
    }
}
Specific product code

Corresponding to BYD, TSLA and other automotive products

public class ConcreteProductA extends Product{<!-- -->
    @Override
    void method() {<!-- -->
        System.out.println("specific product A");
    }
}

public class ConcreteProductB extends Product{<!-- -->
    @Override
    void method() {<!-- -->
        System.out.println("specific product B");
    }
}
Client code implementation
public class Client {<!-- -->
    public static void main(String[] args) {<!-- -->
        Factory factory = new ConcreteFactory();
        Product productA = factory.createProduct(ConcreteProductA.class);
        Product productB = factory.createProduct(ConcreteProductB.class);

        productA.method();//specific product A

        productB.method();//specific product B
    }
}

Compromises in reflection factory methods

Through the way of reflection, it solves the problem of too many product factories caused by too many products in the factory method pattern. However, the Client class has some coupling with the product class. It can only be said that there is no perfect design pattern, only suitable.

Definition of boring

After listening to the story, let’s take a look at the definition and usage scenarios of the official statement.

Related definitions

The factory method pattern is divided into four components, abstract factory, abstract product, concrete factory, and concrete product.
The factory method pattern is a creational design pattern. is to decouple the creation of objects from the Client class.
The factory will return the object to the client, and the client does not know the specific details of the creation.

Usage scenario

1. There are a large number of objects of the same type with the same attributes.
2. The creation of these objects needs to be decoupled from the client.

How to use

1. Extract the common features of objects with the same features. Generate an abstract product class.
2. Generate concrete products that inherit from abstract products.
3. Create an abstract factory to generate abstract methods that return abstract products.
4. Generate a concrete factory that creates a concrete product, which inherits from the abstract factory. Rewrite the abstract method to return the object of the specific product.

Notes

  1. Advantages, separate object creation from client code to achieve decoupling.
  2. The disadvantage is that every time a category is added, an abstract product and abstract factory must be added.
  3. To improve, specific factories can be implemented in a reflective way, instead of creating a corresponding specific factory for each specific product. The amount of code can be reduced.

Summary

So far, the factory method pattern is finished. Simple factory can be regarded as a kind of factory method pattern. If it is to simply create objects of same type, the simple factory pattern will do the job. However, if it is a large project, it is recommended to use the factory method pattern for decoupling. As for whether to use the normal factory method or the reflected factory method, it depends entirely on personal choice. After all, only the suitability is the best!

In addition, the premise of using the factory method pattern is the creation of a large number of objects of the same type. Don’t forget this.