Factory model you can understand

catalogue

1. Case requirements

2. Simple factory mode

2.1 basic introduction

2.2 coding

3. Factory method model

3.1 basic introduction

3.2 coding

4. Abstract factory pattern

4.1 basic introduction

4.2 coding

5. Summary

5.1 significance of factory mode

5.2. Three factory modes

5.3 dependency abstraction principle of design pattern

1. Case requirements

Let's take a specific demand as a case to learn

If there is a pizza project, to facilitate the expansion of pizza types and maintenance, there are the following requirements

  1. There are many kinds of pizza, such as cheese pizza, bacon pizza, fruit pizza and so on

  2. The production of pizza includes the following stages: preparing raw materials, baking, cutting, packing, etc

  3. Pizza ordering function

Now let's start coding for the above requirements

First, we write a Pizza abstract class

package org.colin.factory._01_first;
​
//Abstract Pizza class
public abstract class Pizza {
​
    //Pizza name
    protected String name;
​
    //Prepare raw materials. Different pizzas have different raw materials. Therefore, we make abstract methods and implement classes to select different raw materials
    public abstract void prepare();
​
    //bake
    public void bake(){
        System.out.println("Baking complete");
    }
​
    //cutting
    public void cut(){
        System.out.println("Cutting complete");
    }
​
    //pack
    public void pack(){
        System.out.println("Packaging complete");
    }
​
    //Set pizza name
    public void setName(String name) {
        this.name = name;
    }
}

Then write different kinds of pizza classes to inherit this abstract class and implement the abstract method. First write the cheese pizza class:

package org.colin.factory._01_first;
​
//Cheese pizza
public class CheesePizza extends Pizza{
​
    @Override
    public void prepare() {
        System.out.println("Prepare raw materials for making [cheese] pizza");
    }
}

Bacon pizza:

package org.colin.factory._01_first;
​
//Bacon Pizza
public class BaconPizza extends Pizza{
​
    @Override
    public void prepare() {
        System.out.println("Prepare raw materials for making [bacon] pizza");
    }
}

Fruit pizza:

package org.colin.factory._01_first;
​
//Fruit pizza
public class FruitPizza extends Pizza{
​
    @Override
    public void prepare() {
        System.out.println("Prepare raw materials for making [fruit] pizza");
    }
}

The next step is to complete the pizza order class, where you can create different pizza objects according to the entered pizza types:

package org.colin.factory._01_first;
​
//Order class interface
public class PizzaController {
​
    public static void main(String[] args) {
        OrderPizza("Cheese");//Next order for cheese pizza
    }
​
    //Pizza type: enter pizza type and return different pizza objects
    public static void OrderPizza(String pizzaType) {
        Pizza pizza = null;
        if (pizzaType.equals("Cheese")){
            //Cheese pizza
            pizza = new CheesePizza();
        }else if (pizzaType.equals("Bacon")){
            //Bacon Pizza
            pizza = new BaconPizza();
        }else if (pizzaType.equals("Fruit")){
            //Fruit pizza
            pizza = new FruitPizza();
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.pack();
    }
}

Operation results:

Prepare raw materials for making [cheese] pizza
 Baking complete
 Cutting complete
 Packaging complete

If the OrderPizza parameter in main is passed into Fruit, it will be the order of the next Fruit pizza. The print results are as follows:

Prepare raw materials for making [fruit] pizza
 Baking complete
 Cutting complete
 Packaging complete

So far, we have completed all the requirements. It's done!

But is that all right?

You can look at the above code. The code of our new pizza is written in the pizza order class. Therefore, once we need to expand a new pizza category, in addition to creating a new pizza category class, we also need to modify the pizza order class and add code, which violates the opening and closing principle

At this point, we can consider using the factory mode

2. Simple factory mode

2.1 basic introduction

  1. The simple factory mode belongs to the creation mode, which is a kind of factory mode.

  2. The simple factory pattern is a factory object that determines which product class instance to create.

  3. Simple factory mode is the simplest and most practical mode in the factory mode family.

  4. Simple factory pattern: defines a class for creating objects, which encapsulates the behavior of instantiating objects.

  5. In software development, when we will use a lot of to create a, a class or a batch of objects, we will use the factory pattern.

2.2 coding

How can we use the simple factory method to optimize the above case? First, we need to extract the pizza production code from the pizza order class PizzaController to form a simple factory class:

package org.colin.factory._02_simpleFactory;
​
import org.colin.factory.first.BaconPizza;
import org.colin.factory.first.CheesePizza;
import org.colin.factory.first.FruitPizza;
import org.colin.factory.first.Pizza;
​
//Simple factory mode
public class SimpleFactory {
​
    /**
     * Returns the corresponding Pizza object according to the Pizza type
     * @param pizzaType
     * @return Pizza
     */
    public Pizza createPizza(String pizzaType){
        Pizza pizza = null;
        System.out.println("Use [simple factory mode] to produce pizza.......");
        if (pizzaType.equals("Cheese")){
            //Cheese pizza
            pizza = new CheesePizza();
        }else if (pizzaType.equals("Bacon")){
            //Bacon Pizza
            pizza = new BaconPizza();
        }else if (pizzaType.equals("Fruit")){
            //Fruit pizza
            pizza = new FruitPizza();
        }
        return pizza;
    }
}

The order class PizzaController can be changed to the following:

package org.colin.factory._02_simpleFactory;

import org.colin.factory.first.Pizza;

//Order class interface
public class PizzaController {

    //Define a simple factory object
    private SimpleFactory simpleFactory;

    public PizzaController(SimpleFactory simpleFactory){
        this.simpleFactory = simpleFactory;
    }

    //Pizza type: enter pizza type and return different pizza objects
    public void OrderPizza(String pizzaType) {
        Pizza pizza = this.simpleFactory.createPizza(pizzaType);
        if(pizza != null){
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.pack();
        }else{
            System.out.println("Failed to order pizza");
        }
    }
}

class TestPizza{
    public static void main(String[] args) {
        PizzaController pizzaController = new PizzaController(new SimpleFactory());
        pizzaController.OrderPizza("Cheese");
    }
}

Operation results:

Using [simple factory mode] to produce pizza
 Prepare raw materials for making [cheese] pizza
 Baking complete
 Cutting complete
 Packaging complete

The advantage of this modification is that if we need to expand the new pizza category, we only need to change the code in the simple factory class (i.e. SimpleFactory class), and there is no need to change elsewhere.

Next, for this case, there is a new demand as follows: when ordering Pizza, customers can order Pizza with different flavors, such as cheese Pizza in Beijing, pepper Pizza in Beijing, cheese Pizza in London and pepper Pizza in London.

There are two ideas at this time:

  1. Use the simple factory mode to create different simple factory classes, such as BJPizzaSimpleFactory and ldpizza simplefactory. However, considering the scale of the project and the maintainability and scalability of the software, this idea is not very good

  2. Use factory method mode

3. Factory method model

3.1 basic introduction

Factory method pattern: defines an abstract method to create an object, and the subclass determines the class to be instantiated

For the new requirements of the above case, we use the factory method pattern change. OrderPizza is an abstract factory class, in which the createPizza() method is an abstract method, Then the pizza factory classes of different regional flavors are defined: BJOrderPizza and ldorderpizza. These two pizza factory classes produce pizza in various regions respectively. The class diagram is as follows:

 

3.2 coding

The Pizza abstract class remains unchanged:

package org.colin.factory._03_factoryMethod;

//Abstract Pizza class
public abstract class Pizza {

    //Pizza name
    protected String name;

    //Prepare raw materials. Different pizzas have different raw materials. Therefore, we make abstract methods and implement classes to select different raw materials
    public abstract void prepare();

    //bake
    public void bake(){
        System.out.println("Baking complete");
    }

    //cutting
    public void cut(){
        System.out.println("Cutting complete");
    }

    //pack
    public void pack(){
        System.out.println("Packaging complete");
    }

    //Set pizza name
    public void setName(String name) {
        this.name = name;
    }
}

Bjchiesepizza class:

package org.colin.factory._03_factoryMethod;

//Beijing cheese pizza
public class BJCheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("Beijing flavor [cheese] pizza is preparing raw materials.....");
    }
}

BJBaconPizza class:

package org.colin.factory._03_factoryMethod;

//Beijing style bacon pizza
public class BJBaconPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("Beijing style [bacon] pizza is preparing raw materials.....");
    }
}

BJFruitPizza class:

package org.colin.factory._03_factoryMethod;

//Beijing flavor fruit pizza
public class BJFruitPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("Beijing flavor [fruit] pizza is preparing raw materials.....");
    }
}

Here are three categories of London flavors:

package org.colin.factory._03_factoryMethod;

//London cheese pizza
public class LDCheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("London flavor [cheese] pizza, raw materials are being prepared.....");
    }
}
package org.colin.factory._03_factoryMethod;

//London Style bacon pizza
public class LDBaconPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("London flavor [bacon] pizza, raw materials are being prepared.....");
    }
}
package org.colin.factory._03_factoryMethod;

//London fruit pizza
public class LDFruitPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("London flavor [fruit] pizza, raw materials are being prepared.....");
    }
}

Abstract factory class OrderPizza:

package org.colin.factory._03_factoryMethod;

//Abstract factory class
public abstract class OrderPizza {

    /**
     * Define an abstract method
     * @param pizzaType
     * @return Pizza
     */
    public abstract Pizza createPizze(String pizzaType);

    public OrderPizza(String orderType) {
        //The abstract method is completed by the factory subclass
        Pizza pizza = createPizze(orderType);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.pack();
    }
}

The two factory subclasses implement the OrderPizza class respectively:

package org.colin.factory._03_factoryMethod;

//Beijing taste order
public class BJOrderPizza extends OrderPizza {

    public BJOrderPizza(String orderType) {
        super(orderType);
    }

    @Override
    public Pizza createPizze(String pizzaType) {
        Pizza pizza = null;
        if (pizzaType.equals("Cheese")){
            //Cheese pizza
            pizza = new BJCheesePizza();
        }else if (pizzaType.equals("Bacon")){
            //Bacon Pizza
            pizza = new BJBaconPizza();
        }else if (pizzaType.equals("Fruit")){
            //Fruit pizza
            pizza = new BJFruitPizza();
        }
        return pizza;
    }
}
package org.colin.factory._03_factoryMethod;

//London taste order
public class LDOrderPizza extends OrderPizza {

    public LDOrderPizza(String orderType) {
        super(orderType);
    }

    @Override
    public Pizza createPizze(String pizzaType) {
        Pizza pizza = null;
        if (pizzaType.equals("Cheese")){
            //Cheese pizza
            pizza = new LDCheesePizza();
        }else if (pizzaType.equals("Bacon")){
            //Bacon Pizza
            pizza = new LDBaconPizza();
        }else if (pizzaType.equals("Fruit")){
            //Fruit pizza
            pizza = new LDFruitPizza();
        }
        return pizza;
    }
}

Test class:

package org.colin.factory._03_factoryMethod;

public class PizzaTest {

    public static void main(String[] args) {
        //Create all kinds of pizza with Beijing flavor
        new LDOrderPizza("Cheese");
    }
}

4. Abstract factory pattern

4.1 basic introduction

  1. The abstract factory pattern defines an interface for creating clusters of related or dependent objects without specifying specific classes

  2. Abstract factory pattern can integrate simple factory pattern and factory method pattern

  3. From the design level, the abstract factory pattern is an improvement (or further abstraction) of the simple factory pattern

  4. The factory is abstracted into two layers, the abstract factory and the factory subclass of concrete implementation. Programmers can use the corresponding factory subclasses according to the type of object they create. This turns a single simple factory class into a factory cluster, which is more conducive to code maintenance and expansion

After the above pizza case uses the abstract factory mode, the class diagram is as follows:

 

4.2 coding

Next, let's implement it. The abstract factory interface is as follows:

package org.colin.factory._04_abstractFactory;

//Abstract layer of abstract factory pattern
public interface AbsFactory {

    /**
     * Implemented by the following concrete subclasses
     * @param pizzaType
     * @return
     */
    Pizza createPizza(String pizzaType);
}

Two specific plant subclasses:

package org.colin.factory._04_abstractFactory;

//Specific factory subclass
public class BJFactory implements AbsFactory{

    @Override
    public Pizza createPizza(String pizzaType) {
        System.out.println("The abstract factory pattern is used");
        Pizza pizza = null;
        if (pizzaType.equals("Cheese")){
            //Cheese pizza
            pizza = new BJCheesePizza();
        }else if (pizzaType.equals("Bacon")){
            //Bacon Pizza
            pizza = new BJBaconPizza();
        }else if (pizzaType.equals("Fruit")){
            //Fruit pizza
            pizza = new BJFruitPizza();
        }
        return pizza;
    }
}
package org.colin.factory._04_abstractFactory;

//Specific factory subclass
public class LDFactory implements AbsFactory{

    @Override
    public Pizza createPizza(String pizzaType) {
        System.out.println("The abstract factory pattern is used");
        Pizza pizza = null;
        if (pizzaType.equals("Cheese")){
            //Cheese pizza
            pizza = new LDCheesePizza();
        }else if (pizzaType.equals("Bacon")){
            //Bacon Pizza
            pizza = new LDBaconPizza();
        }else if (pizzaType.equals("Fruit")){
            //Fruit pizza
            pizza = new LDFruitPizza();
        }
        return pizza;
    }
}

Pizza order category:

package org.colin.factory._04_abstractFactory;

//Pizza order class
public class OrderPizza {

    public AbsFactory absFactory;

    public OrderPizza(AbsFactory absFactory){
        this.absFactory = absFactory;
    }

    public void setOrder(String pizzaType){
        Pizza pizza = this.absFactory.createPizza(pizzaType);
        if (pizza != null){
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.pack();
        }else{
            System.out.println("Failed to order pizza");
        }
    }
}

Test class:

package org.colin.factory._04_abstractFactory;

public class Test {

    public static void main(String[] args) {
        //Here is a demonstration of the factory subclass in Beijing
        OrderPizza orderPizza = new OrderPizza(new BJFactory());
        orderPizza.setOrder("Cheese");
    }
}

Operation results:

The abstract factory pattern is used
 Beijing flavor [cheese] pizza is preparing raw materials
 Baking complete
 Cutting complete
 Packaging complete

5. Summary

5.1 significance of factory mode

Abstract the code of the instantiated object and put it into a class for unified management and maintenance, so as to decouple the dependency relationship with the main project, so as to improve the expansion and maintenance of the project.

5.2. Three factory modes

  • Simple factory mode

  • Factory method model

  • Abstract factory pattern

5.3 dependency abstraction principle of design pattern

  • When creating an object instance, do not directly the new class, but put the action of the new class in a factory method and return. It can also be said that variables do not directly hold references to specific classes.

  • Instead of letting classes inherit concrete classes, they inherit abstract classes or implement interface s.

  • Do not override methods already implemented in the base class.

Keywords: Java Design Pattern

Added by BigDaddy13 on Mon, 03 Jan 2022 14:14:06 +0200