Template mode of design mode

Project source code:

CSDN : http://jacob.org.cn

Nuwa's mistake

The factory model tells the story of Nuwa making people. People are created and the world is lively, but when she looks down, they are all the same type. They lack love, hatred, joys, sorrows and other emotions. Human life is peaceful and light. Nu Wa suddenly patted her head and forgot to define gender for human beings. What should she do? Erase and start again, so mankind has gone through a great baptism, all races have been eliminated, and the world is empty, silent and lonely.

Because Nuwa spent a lot of energy on the preparation work before, such as preparing loess and Bagua stove, it is impossible to build all things from scratch, so we want to recreate people under the existing conditions and make use of old things as much as possible. How should the race (Product category) be transformed? How can human beings have love and hate? Of course, there is a way for immortals to define mutually exclusive gender, and then plant a seed in each individual: the opposite sex attracts each other, and when they mature, they will find a opposite sex (this is what we call the driving force of love). From a design point of view, a specific object can be determined through two coordinates: skin color and gender.

After the product category analysis is completed, how should the factory category (Bagua furnace) be transformed? There is only one production equipment, which either produces all men or all women. That won't work. Such earth shaking transformation is to produce human beings of different genders. There's a way! Take apart the existing production equipment - Bagua stove, so Nuwa used the "Bagua reproduction technique", changing the original Bagua stove into two and slightly modifying it to become a female Bagua stove (only producing female race) and a male Bagua stove (only producing male race). Therefore, Nuwa began to prepare for production. Its class diagram is shown in Figure 9-2.

Although this class diagram is large, it is relatively simple. The typical class diagram of Java includes one interface, multiple abstract classes, and then N implementation classes. Each race is an abstract class, and gender is implemented in each implementation class. In particular, the HumanFactory interface defines three methods, which are respectively used to produce three races with different skin colors, that is, the Y coordinate in Figure 9-1. Its two implementation classes are gender, that is, the X coordinate in Figure 9-1, which uniquely determines a produced object through the X coordinate (gender) and Y coordinate (skin color). Let's take a look at the related implementation, the Human interface.

/**
 * @program: DesignMode
 * @description: Ethnic interface
 * @author: Jacob
 * @create: 2020-08-18 10:57
 **/
public interface Human {

    //Each race has its own color
    public void getColor();

    //Humans can talk
    public void talk();

    //Everyone has a gender
    public void getSex();

}

Race has three abstract classes, which are responsible for the definition of abstract attributes of race: skin color and language. White race, black race and yellow race are shown in the code respectively.

/**
 * @program: DesignMode
 * @description: Black race
 * @author: Jacob
 * @create: 2020-08-18 11:14
 **/
public abstract class AbstractBlackHuman implements Human{

    //The skin color of black people is black
    @Override
    public void getColor() {
        System.out.println("The skin color of black people is black!");
    }

    //Black speech
    @Override
    public void talk() {
        System.out.println("Black people can speak, but ordinary people can't understand.");
    }

}
/**
 * @program: DesignMode
 * @description: white race
 * @author: Jacob
 * @create: 2020-08-18 11:13
 **/
public abstract class AbstractWhiteHuman implements Human {

    //The skin color of white people is white
    @Override
    public void getColor() {
        System.out.println("The skin color of white people is white!");
    }

    //White speech
    @Override
    public void talk() {
        System.out.println("White people can talk. Generally speaking, they are single bytes.");
    }

}
/**
 * @program: DesignMode
 * @description: yellow race
 * @author: Jacob
 * @create: 2020-08-18 11:16
 **/
public abstract class AbstractYellowHuman implements Human {

    //The skin color of the yellow race is yellow
    @Override
    public void getColor() {
        System.out.println("The skin color of yellow people is yellow!");
    }

    //Yellow speech
    @Override
    public void talk() {
        System.out.println("Yellow people can talk. Generally speaking, they are double bytes.");
    }
    
}

Each abstract class has two implementation classes, which respectively implement the most detailed and concrete things in common: skin color and language. The specific implementation class implements the definition of skin color and gender.

/**
 * @program: DesignMode
 * @description: Black female race
 * @author: Jacob
 * @create: 2020-08-18 11:21
 **/
public class FemaleBlackHuman extends AbstractBlackHuman {
    @Override
    public void getSex() {
        System.out.println("Black female race");
    }
}
/**
 * @program: DesignMode
 * @description: Black Male Race
 * @author: Jacob
 * @create: 2020-08-18 11:22
 **/
public class MaleBlackHuman extends AbstractBlackHuman{
    @Override
    public void getSex() {
        System.out.println("Black Male Race");
    }
}
/**
 * @program: DesignMode
 * @description: White female race
 * @author: Jacob
 * @create: 2020-08-18 11:20
 **/
public class FemaleWhiteHuman extends AbstractWhiteHuman{
    @Override
    public void getSex() {
        System.out.println("White female race");
    }
}
/**
 * @program: DesignMode
 * @description: White male race
 * @author: Jacob
 * @create: 2020-08-18 11:20
 **/
public class MaleWhiteHuman extends AbstractWhiteHuman{
    @Override
    public void getSex() {
        System.out.println("White male race");
    }
}
/**
 * @program: DesignMode
 * @description: Yellow female race
 * @author: Jacob
 * @create: 2020-08-18 11:17
 **/
public class FemaleYellowHuman extends AbstractYellowHuman {

    @Override
    public void getSex() {
        System.out.println("Yellow female race");
    }
}
/**
 * @program: DesignMode
 * @description: Yellow male race
 * @author: Jacob
 * @create: 2020-08-18 11:18
 **/
public class MaleYellowHuman extends AbstractYellowHuman {
    @Override
    public void getSex() {
        System.out.println("Yellow male race");
    }
}

So far, we have defined all the races in the real world, and the rest of the work is how to make humans.

/**
 * @program: DesignMode
 * @description: Bagua furnace definition
 * @author: Jacob
 * @create: 2020-08-18 17:47
 **/
public interface HumanFactory {

    //Create a yellow race
    public Human createYellowHuman();

    //Create a white race
    public Human createWhiteHuman();

    //Create a black race
    public Human createBlackHuman();

}

In the interface, we can see that the Bagua stove can produce people with different skin colors (of course, Nuwa's mistake). How many Bagua stoves does it have? Two, respectively, produce female and male, female and male gossip stove.

/**
 * @program: DesignMode
 * @description: Gossip stove for producing women
 * @author: Jacob
 * @create: 2020-08-18 17:48
 **/
public class FemaleFactory implements HumanFactory {

    //Produce yellow women
    @Override
    public Human createYellowHuman() {
        return new FemaleYellowHuman();
    }

    //Produce white women
    @Override
    public Human createWhiteHuman() {
        return new FemaleWhiteHuman();
    }

    //Produce black women
    @Override
    public Human createBlackHuman() {
        return new FemaleBlackHuman();
    }
}
/**
 * @program: DesignMode
 * @description: Bagua stove for producing men
 * @author: Jacob
 * @create: 2020-08-18 17:49
 **/
public class MaleFactory implements HumanFactory {

    //Produce black men
    @Override
    public Human createBlackHuman() {
        return new MaleBlackHuman();
    }

    //Produce white men
    @Override
    public Human createWhiteHuman() {
        return new MaleWhiteHuman();
    }

    //Produce yellow men
    @Override
    public Human createYellowHuman() {
        return new MaleYellowHuman();
    }

}

With the race and the Bagua stove, let's recreate the scene of Nu Wa making people in those days.

/**
 * @program: DesignMode
 * @description: Nuwa remakes mankind
 * @author: Jacob
 * @create: 2020-08-18 17:51
 **/
public class NvWa {
    
    public static void main(String[] args) {
        //First production line, male production line
        HumanFactory maleHumanFactory = new MaleFactory();
        //Second production line, female production line
        HumanFactory femaleHumanFactory = new FemaleFactory();
        //After the establishment of the production line, the production started
        Human femaleYellowHuman = femaleHumanFactory.createYellowHuman();
        Human femaleBlackHuman = femaleHumanFactory.createBlackHuman();
        Human femaleWhiteHuman = femaleHumanFactory.createWhiteHuman();
        System.out.println("---Give birth to a yellow woman---");
        femaleYellowHuman.getColor();
        femaleYellowHuman.talk();
        femaleYellowHuman.getSex();
        System.out.println("---Give birth to a black woman---");
        femaleBlackHuman.getColor();
        femaleBlackHuman.talk();
        femaleBlackHuman.getSex();
        System.out.println("---Give birth to a white woman---");
        femaleWhiteHuman.getColor();
        femaleWhiteHuman.talk();
        femaleWhiteHuman.getSex();
        Human maleYellowHuman = maleHumanFactory.createYellowHuman();
        Human maleBlackHuman = maleHumanFactory.createBlackHuman();
        Human maleWhiteHuman = maleHumanFactory.createWhiteHuman();
        System.out.println("\n---Give birth to a yellow man---");
        femaleWhiteHuman.getColor();
        maleYellowHuman.talk();
        maleYellowHuman.getSex();
        System.out.println("\n---Give birth to a yellow man---");
        maleBlackHuman.getColor();
        maleBlackHuman.talk();
        maleBlackHuman.getSex();
        System.out.println("\n---Give birth to a white man---");
        maleWhiteHuman.getColor();
        maleWhiteHuman.talk();
        maleWhiteHuman.getSex();
    }
    
}
The operation results are as follows:
---Give birth to a yellow woman---
The skin color of yellow people is yellow!
Yellow people can talk. Generally speaking, they are double bytes.
Yellow female race
---Give birth to a black woman---
The skin color of black people is black!
Black people can speak, but ordinary people can't understand.
Black female race
---Give birth to a white woman---
The skin color of white people is white!
White people can talk. Generally speaking, they are single bytes.
White female race

---Give birth to a yellow man---
The skin color of white people is white!
Yellow people can talk. Generally speaking, they are double bytes.
Yellow male race

---Give birth to a yellow man---
The skin color of black people is black!
Black people can speak, but ordinary people can't understand.
Black Male Race

---Give birth to a white man---
The skin color of white people is white!
White people can talk. Generally speaking, they are single bytes.
White male race

Men and women of all skin colors are created, and there is mutual attraction between the two sexes, so emotion arises, and there is a novel theme "love" in the world. Looking back at our design, I don't know if you have ever been to the factory. Each factory is divided into many workshops, and each workshop is divided into multiple production lines to produce different products. We can compare the Bagua stove to a workshop, and call the production process of Bagua stove (white, black or yellow) as a production line. In this way, it is a female production workshop, which specializes in producing women of all skin colors, One is the male production workshop, which specializes in producing men of various skin colors. After production, they can be assembled outside the system. What is assembly? Hey, hey, think for yourself! Under this design, the responsibilities of each workshop and each production line are very clear, and the products produced in the workshop can have coupling relationship. You should know that the proportion of black, yellow and white species in the world is 1:4:6, which requires Nuwa to do a good job in proportion distribution and coordination in one workshop. This is the abstract factory pattern.

Definition of abstract factory pattern

Abstract Factory Pattern is a common pattern, which is defined as follows:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes. (provide an interface for creating a set of related or interdependent objects without specifying their specific classes.)

Abstract factory pattern is an upgraded version of factory method pattern. When there are multiple business varieties and business classifications, generating required objects through abstract factory pattern is a very good solution. Let's take a look at the general source code of the abstract factory. First, there are two interacting product lines (also known as product families), such as the left door and the right door for manufacturing cars. These two should be equal in number - the constraints between the two objects and the doors of each model are different, which are constrained by the product hierarchy. Let's take a look at the class diagrams of the two product families.

Note that the circles and frames on the class diagram correspond. Two abstract product classes can be related, such as jointly inheriting or implementing an abstract class or interface.

/**
 * @program: DesignMode
 * @description: Abstract product class A
 * @author: Jacob
 * @create: 2020-08-18 17:57
 **/
public abstract class AbstractProductA {
    //Methods common to each product
    public void shareMethod() {
        System.out.println("Each product A Common method");
    }

    //Each product has the same method and different implementation
    public abstract void doSomething();
}
/**
 * @program: DesignMode
 * @description: Implementation class of product A1
 * @author: Jacob
 * @create: 2020-08-18 17:58
 **/
public class ProductA1 extends AbstractProductA {
    @Override
    public void doSomething() {
        System.out.println("product A1 Implementation method of");
    }
}
/**
 * @program: DesignMode
 * @description: Implementation class of product A1
 * @author: Jacob
 * @create: 2020-08-18 17:58
 **/
public class ProductA2 extends AbstractProductA {
    @Override
    public void doSomething() {
        System.out.println("product A2 Implementation method of");
    }
}
/**
 * @program: DesignMode
 * @description: Abstract product class B
 * @author: Jacob
 * @create: 2020-08-18 17:57
 **/
public abstract class AbstractProductB {
    //Methods common to each product
    public void shareMethod() {
        System.out.println("Each product B Common method");
    }

    //Each product has the same method and different implementation
    public abstract void doSomething();
}
/**
 * @program: DesignMode
 * @description: Product B1 implementation class
 * @author: Jacob
 * @create: 2020-08-18 17:58
 **/
public class ProductB1 extends AbstractProductB {
    @Override
    public void doSomething() {
        System.out.println("product B1 Implementation method of");
    }
}
/**
 * @program: DesignMode
 * @description: Product B2 implementation class
 * @author: Jacob
 * @create: 2020-08-18 17:58
 **/
public class ProductB2 extends AbstractProductB {
    @Override
    public void doSomething() {
        System.out.println("product B2 Implementation method of");
    }
}

Abstract factory class AbstractCreator is responsible for defining the functions to be implemented by each factory. In general code, abstract factory class defines the product creation of two product families.

/**
 * @program: DesignMode
 * @description: Abstract factory class
 * @author: Jacob
 * @create: 2020-08-18 18:02
 **/
public abstract class AbstractCreator {

    //Create A product family
    public abstract AbstractProductA createProductA();

    //Create B product family
    public abstract AbstractProductB createProductB();

}

Note: if there are N product families, there should be N creation methods in the abstract factory class.

How to create a product is completed by specific implementation classes.

/**
 * @program: DesignMode
 * @description: Implementation class of product level 1
 * @author: Jacob
 * @create: 2020-08-18 18:03
 **/
public class Creator1 extends AbstractCreator {
    //Only product A with product grade 1 is produced
    @Override
    public AbstractProductA createProductA() {
        return new ProductA1();
    }

    //Only B products with product grade 1 are produced
    @Override
    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}
/**
 * @program: DesignMode
 * @description: Implementation class of product level 1
 * @author: Jacob
 * @create: 2020-08-18 18:03
 **/
public class Creator2 extends AbstractCreator {
    //Only product A with product grade 1 is produced
    @Override
    public AbstractProductA createProductA() {
        return new ProductA2();
    }

    //Only B products with product grade 1 are produced
    @Override
    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}

Note: if there are M product levels, there should be m implementation factory classes. In each implementation factory, the production tasks of different product families should be realized.

How to generate an implementation independent object in a specific business?

/**
 * @program: DesignMode
 * @description: Scene class
 * @author: Jacob
 * @create: 2020-08-18 18:08
 **/
public class Client {

    public static void main(String[] args) {
        //Define two factories
        AbstractCreator creator1 = new Creator1();
        AbstractCreator creator2 = new Creator2();
        //Generate A1 object
        AbstractProductA a1 = creator1.createProductA();
        a1.doSomething();
        a1.shareMethod();
        //Generate A2 object
        AbstractProductA a2 = creator2.createProductA();
        a2.doSomething();
        a2.shareMethod();
        //Generate B1 object
        AbstractProductB b1 = creator1.createProductB();
        b1.doSomething();
        b1.shareMethod();
        //Generate B2 object
        AbstractProductB b2 = creator2.createProductB();
        b2.doSomething();
        b2.shareMethod();
    }
}

In the scenario class, no method is related to the implementation class. For a product, as long as we know its factory method, we can directly generate a product object without caring about its implementation class.

The operation results are as follows:
product A1 Implementation method of
 Each product A Common method
 product A2 Implementation method of
 Each product A Common method
 product B1 Implementation method of
 Each product B Common method
 product B2 Implementation method of
 Each product B Common method

Application of abstract factory pattern

Advantages of abstract factory pattern

  • Encapsulation. The implementation class of each product is not what the high-level module should care about. What should it care about? It is an interface and an abstraction. It doesn't care how objects are created. Who is responsible for this? Factory class, as long as I know who the factory class is, I can create a needed object, which saves time and effort. Excellent design should be like this.

  • Constraints within the product family are non-public. For example, on the issue of the proportion of men and women in production, I guess Nuwa must have her own plan. She can't let women prosper and men decline, otherwise the advantages of women can't be reflected? In the abstract factory mode, there should be such a constraint: every time a woman is produced, 1.2 men are produced at the same time. Such a production process is transparent for calling the high-level module of the factory class. It does not need to know this constraint. I just want a yellow female product. The constraints in the specific product family are implemented in the factory.

Disadvantages of abstract factory pattern

The biggest disadvantage of the abstract factory pattern is that it is very difficult to expand the product family. Why do you say so? Let's take the general code as an example. If we want to add a product C, that is, the product family will be increased from 2 to 3. Let's see how much our program has changed! Abstract class AbstractCreator needs to add a method createProductC(), and then both implementation classes need to be modified. Think about it, this seriously violates the opening and closing principle, and we have always explained that abstract classes and interfaces are a contract. If the contract is changed, all the code related to the contract must be repaired. What is the name of this code? It's called "toxic code", - as long as it has something to do with this code, there may be a risk of infringement!

Usage scenario of abstract factory pattern

The usage scenario definition of the abstract factory pattern is very simple: if an object family (or a group of objects without any relationship) has the same constraints, the abstract factory pattern can be used. What do you mean? For example, a text editor and a picture processor are both software entities, but although the functions and interfaces of the text editor under * nix and the text editor under Windows are the same, the code implementation is different, and the picture processor also has a similar situation. That is, it has a common constraint: operating system type. So we can use the abstract factory mode to produce editors and image processors under different operating systems.

Considerations for abstract factory patterns

Among the shortcomings of the abstract factory pattern, we mentioned that it is difficult to expand the product family of the abstract factory pattern, but it must be clear that it is the difficulty of product family expansion, not the product level. In this mode, the product level is very easy to expand. To add a product level, just add a factory class to be responsible for the newly added product production task. In other words, horizontal expansion is easy and vertical expansion is difficult. Taking human beings as an example, there are only two genders in the product level: men and women. In the real world, there is another gender: bisexuals, both men and women (as the saying goes, yin-yang people). It is also very easy for us to expand this product level. Add three product categories corresponding to different skin colors, and then create a factory category, which is specially responsible for the creation of bisexuals with different skin colors, From this point of view, the abstract factory pattern is in line with the opening and closing principle.

Best practices

When a pattern can be used is a puzzle for many readers. Abstract factory mode is a simple mode, which is used in many scenarios. In the process of software product development, when different operating systems are involved, you can consider using abstract factory mode. For example, an application needs to run on three different platforms (Windows, Linux and Android (intelligent terminal operating system released by Google). How would you design it? Design three different applications? No, the impact of the operating system on the application is shielded through the abstract factory pattern. The software functions, application logic and UI of three different operating systems should be very similar. The only difference is to call different factory methods and use different product classes to process the information interacting with the operating system.

Learning from: Zen of design pattern - Qin Xiaobo

Tags: Design Pattern

Posted by imartin on Sun, 22 May 2022 15:51:08 +0300