“This is the 13th day of my Participation in the August More Text Challenge. Check out the details:August is more challenging”

First, the historical origin of the factory model

In real life, we all know that the primitive society is self-sufficient (no factory), the farming society small workshop (simple factory, folk wine workshop), the industrial revolution assembly line (factory method, self-produced and self-sold).

Our project code also iterated from simplicity to complexity, but it became more and more simple for the caller.

Second, simple factory mode

The Simple Factory Pattern is one in which a Factory object determines which instances of a product class are created, but it does not belong to the GOF 23 design patterns. A simple factory is suitable for scenarios where the factory class is responsible for creating fewer objects, and the client only needs to pass in the factory class parameters and does not care about the logic of how the object is created.

Let’s take the course as an example. At present, Java, front-end and other courses, has formed an ecology. We can define a course standard ICourse interface:

public interface ICourse {
    // Play the lesson
    void record(a);
}
Copy the code

Create Java course implementation class JavaCourse:

public class JavaCourse implements ICourse {
    @Override
    public void record(a) {
        System.out.println("Play Java Lessons"); }}Copy the code

Client-side invocation code

public class SimpleFactoryTest {

    @Test
    public void Test(a) {
        ICourse course = newJavaCourse(); course.record(); }}Copy the code

We find that the parent ICourse is a reference to the subclass JavaCourse, that the application layer needs to follow JavaCourse, and that if the business expands and we continue to add AICourse courses and more, our client dependencies will become more and more bloated. Therefore, find ways to reduce the dependency and hide the creation details. Although our process of creating objects is not complicated in the current code, it is not easy to extend from a code design perspective. Now, let’s optimize the code in simple factory mode. Add PythonCourse class:

public class PythonCourse implements ICourse {
    @Override
    public void record(a) {
        System.out.println("Play Python Lessons"); }}Copy the code

Create the CourseFactory factory class:

public class CourseFactory {

    public static ICourse create(String name) {
        if ("java".equalsIgnoreCase(name)) {
            return new JavaCourse();
        } else if ("python".equalsIgnoreCase(name)) {
            return new PythonCourse();
        } else {
            return null; }}}Copy the code

Modify the client call code:

public class SimpleFactoryTest {

    @Test
    public void test(a) {
        ICourse course = CourseFactory.create("python"); course.record(); }}Copy the code

The client-side invocation is simple, but if our business continues to expand to include front-end courses, then the create() method in the factory will have to change the code logic each time, depending on the richness of the product chain. Inconsistent with the open closed principle. Therefore, we can continue to optimize the simple factory by using the reflection technology:

public class CourseFactory {

    public static ICourse create(String className) {
        try {
            if(! (null == className || "".equals(className))) {
                return(ICourse) Class.forName(className).newInstance(); }}catch (Exception e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

Modify the client call code:

public class SimpleFactoryTest {

    @Test
    public void test(a) {
        ICourse course = CourseFactory.create("com.mark.factory.simple.JavaCourse"); course.record(); }}Copy the code

After optimization, the product is continuously enriched without the need to modify the code in CourseFactory. However, the problem is that the method parameters are strings, the controllability needs to be improved, and the transformation needs to be forced. Let’s change the code again:

public class CourseFactory {

    public static ICourse create(Class<? extends ICourse> clazz) {
        try {
            if(clazz ! =null) {
                returnclazz.newInstance(); }}catch (Exception e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

Optimize the client code:

public class SimpleFactoryTest {

    @Test
    public void test(a) { ICourse course = CourseFactory.create(PythonCourse.class); course.record(); }}Copy the code

3. Factory method mode

Factory Method patterns define an interface for creating an object, but let the class implementing the interface decide which class to instantiate. Factory methods defer class instantiation to subclasses. In the factory method pattern, the user only needs to care about the factory corresponding to the required product, without caring about the creation details, and the addition of new products conforms to the open closed principle.

The factory approach pattern mainly addresses the problem of product extension. In a simple factory, as the product chain becomes richer, if the creation logic of each course is different, the factory becomes more and more responsible, a bit like a universal factory, which is not easy to maintain. Based on the single responsibility principle, we continue to divide the functions, and assign one person to the other. The Java course is created by the Java factory, and the Python course is created by the Python factory, with an abstraction of the factory itself.

Let’s look at the code and create the ICourseFactory interface:

public interface ICourseFactory {
    ICourse create(a);
}
Copy the code

JavaCourseFactory class JavaCourseFactory class JavaCourseFactory class

public class JavaCourseFactory implements ICourseFactory {
    @Override
    public ICourse create(a) {
        return newJavaCourse(); }}Copy the code

PythonCourseFactory class:

public class PythonCourseFactory implements ICourseFactory {
    @Override
    public ICourse create(a) {
        return newPythonCourse(); }}Copy the code

Test code:

public class FactoryMethodTest {

    @Test
    public void test(a) {
        ICourseFactory factory = new JavaCourseFactory();
        ICourse course = factory.create();
        course.record();

        factory = newPythonCourseFactory(); course = factory.create(); course.record(); }}Copy the code

Four,

Simple factories have their drawbacks: factory classes have a relatively heavy burden of responsibility and are less likely to extend an overly complex product structure.

The factory approach applies to the following scenarios:

  1. Creating objects requires a lot of repetitive code.
  2. The client (application layer) does not depend on the details of how the product class instance is created, implemented, etc.
  3. A class uses its subclasses to specify which objects to create.

The factory approach also has disadvantages:

  1. It’s easy to have too many classes, adding complexity.
  2. Increases the abstractness and difficulty of understanding the system.

Welcome to follow the wechat official account (MarkZoe) to learn from each other and communicate with each other.