[Java design pattern] explain three factory patterns in detail with pizza ordering cases

Posted by michaelk46 on Sun, 16 Jan 2022 16:04:24 +0100

  • πŸ‘ About the author: Hello, I'm cabbage ~ ~, a sophomore in school, and a new star creator in the Java field.
  • πŸ“ Personal homepage: Cabbage CSDN blog
  • πŸ“• Series column: This article is written in the Java design pattern column: Isn't that the Java design pattern
  • πŸ“§ If there are mistakes in the knowledge points of the article, please correct them! Learn and make progress with you πŸ‘€
  • πŸ”₯ If you feel the blogger's article is good, please πŸ‘ Three company support πŸ‘ Check out the blogger

preface

Today, I learned the factory pattern in Java design pattern and sorted out the teacher's notes; The class diagram in the article (I drew it lazily) can be used for your reference to the relationship between classes. I hope you like it.

1, Using traditional methods

Class diagram

Step summary

Step 1: create a Pizza abstract class

public abstract class Pizza {
    public String name;
    public abstract void prepare();
    public void bake() {
        System.out.println(name + " baking");
    }
    public void cut() {
        System.out.println(name + " cutting");
    }
    public void box() {
        System.out.println(name + " boxing");
    }
    public void setName(String name) {
        this.name = name;
    }
}

Analysis: this class is used to represent the whole process of making pizza: prepare(), bake(), cut() and box(). Assuming that the materials required in each pizza preparation stage are different, the preparation stage is defined as an abstract method, and the other three stages are the same.

Step 2: create two pizza classes

public class CheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("Cheese pizza is being prepared");
    }
}

Analysis: this class represents cheese pizza. Simply rewrite the preparation stage

public class BeefPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("Beef pizza is being prepared");
    }
}

Analysis: this class represents beef pizza. It's also a simple rewrite of the preparation stage

Step 3: make an order for pizza

public class OrderPizza {
    public OrderPizza() {
        Pizza pizza = null;
        do {
            String pizzaType = getType();
            if ("cheese".equalsIgnoreCase(pizzaType)) {
                 pizza = new CheesePizza();
                 pizza.setName("cheese");
            } else if ("beef".equalsIgnoreCase(pizzaType)) {
                pizza = new BeefPizza();
                pizza.setName("beef");
            } else {
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }
    // Write a method to get the type of pizza you want to order
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

Analysis: looking at the code slowly, you can see that the logic code for ordering pizza is written in the constructor of this class, and the getType() method is used to obtain the type of pizza you want to order. However, if we need to add new pizza, we need to continue to add corresponding logical statements from this class, so as to modify the pizza ordering class, which violates the OCP principle

Step 4: create a run class

public class PizzaStore {
    public static void main(String[] args) {
       new OrderPizza();
    }
}

Operation results:

Analysis of advantages and disadvantages

  • Advantages: easy to understand, simple and easy to operate
  • Disadvantages: it violates the ocp principle of design pattern, that is, it is open to extension and closed to modification. That is, when we add new functions to the class, we try not to modify the code or modify the code as little as possible

2, Use simple factory

Class diagram

Basic introduction

  • The simple factory mode belongs to the creation mode, which is a kind of factory mode. The simple factory pattern is a factory object that determines which product class instance to create. The simple factory pattern is the simplest and most practical pattern in the factory pattern family
  • The simple factory pattern defines a class for creating objects, which encapsulates the behavior (code) of instantiating objects
  • 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

Step summary

Step 1: create a simple factory

public class SimpleFactory {
    public static Pizza createPizza2(String orderType) {
        Pizza pizza = null;
        if ("beef".equalsIgnoreCase(orderType)) {
            pizza = new BeefPizza();
            pizza.setName(" beef ");
        } else if ("cheese".equalsIgnoreCase(orderType)) {
            pizza = new CheesePizza();
            pizza.setName("cheese");
        }
        return pizza;
    }
}

Analysis: a simple factory is also called a static factory. We write a static method to facilitate the call of the following code. The classes used here are the same as those used by traditional methods, and have not changed

Step 2: make an order for pizza

public class OrderPizza2 {
    public OrderPizza2() {
        do {
            String orderType = getType();
            Pizza pizza = SimpleFactory.createPizza2(orderType);
            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println(" Failed to order pizza ");
                break;
            }
        } while (true);
    }
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

Analysis: the constructor of this class uses the simple factory class SimpleFactory, so we don't need to write the code to add new pizza in the behavior of ordering pizza, but write the code to add new pizza from the factory, so we don't need to change this class.

Operation results:

Advantage analysis

  • Using simple factory mode to create objects is more convenient and flexible, and there is no need to modify the logic of ordering pizza

3, Using factory methods

New requirements

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

Class diagram

Basic introduction

  • Factory method pattern design scheme: abstract the instantiation function of pizza project into abstract methods and implement them in different taste ordering subclasses.
  • Factory method pattern: defines an abstract method to create an object, and the subclass determines the class to be instantiated. The factory method pattern defers instantiation of objects to subclasses.

Step summary

Step 1: create four pizza classes

public class BJCheesePizza extends Pizza {
    @Override
    public void prepare() {
        setName("Beijing cheese pizza");
        System.out.println("Beijing cheese pizza Prepare raw materials");
    }
}

Analysis: the Pizza class is the same as the code above. I didn't write it again. This class is used to create cheese flavored Pizza in Beijing

public class BJPepperPizza extends Pizza{
    @Override
    public void prepare() {
        setName("Pepper in Beijing pizza");
        System.out.println("Pepper in Beijing pizza Prepare raw materials");
    }
}

Analysis: this class is used to create chili pizza in Beijing

public class LDCheesePizza extends Pizza {
    @Override
    public void prepare() {
        setName("London cheese pizza");
        System.out.println("London cheese pizza Prepare raw materials");
    }
}

Analysis: this class is used to create cheese flavored pizza in London

public class LDPepperPizza extends Pizza {
    @Override
    public void prepare() {
        setName("Pepper in London pizza");
        System.out.println("Pepper in London pizza Prepare raw materials");
    }
}

Analysis: this class is used to create chili pizza in London

Step 2: create an abstract class for ordering pizza

public abstract class OrderPizza {
    abstract Pizza createPizza(String orderType);
    public OrderPizza() {
        do {
            String orderType = getType();
            Pizza pizza = createPizza(orderType); //Abstract method, which is completed by the factory subclass
            if (pizza == null){
                System.out.println("Failed to order pizza");
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

Analysis: an abstract method createPizza() is defined in this class, which allows each factory subclass to implement itself, and the code logic for ordering pizza is written in the constructor; The getType() method is no different from the original one.

public class BJOrderPizza extends OrderPizza {
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")) {
            pizza = new BJCheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}

Analysis: this class is used to inherit the OrderPizza class and become a pizza ordering distributor in Beijing

public class LDOrderPizza extends OrderPizza {
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")) {
            pizza = new LDCheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}

Analysis: this class is also used to inherit the OrderPizza class and become a pizza ordering distributor in London

Step 3: create a run class

public class PizzaStore {
    public static void main(String[] args) {
        String loc = "beijing";
        if (loc.equals("beijing")) {
            new BJOrderPizza();
        } else {
            new LDOrderPizza();
        }
    }
}

Analysis: suppose you buy pizza in Beijing

Operation results:

4, Use abstract factory

Class diagram

Basic introduction

  • The abstract factory pattern defines an interface for creating clusters of related or dependent objects without specifying specific classes
  • Abstract factory pattern can integrate simple factory pattern and factory method pattern
  • From the design level, the abstract factory pattern is an improvement (or further abstraction) of the simple factory pattern
  • The factory is abstracted into two layers, absfactory (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.

Step summary

Step 1: create a general factory interface

public interface AbsFactory {
    public Pizza createPizza(String orderType);
}

Analysis: this class is used to implement the following factory subclasses

Step 2: create a labor division factory

public class BJFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")) {
            pizza = new BJCheesePizza();
        } else if (orderType.equals("pepper")){
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}

Analysis: This is a factory subclass, which is used to make pizza in Beijing

public class LDFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")) {
            pizza = new LDCheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}

Analysis: This is a factory subclass used to make pizza in London

Step 3: create a subscription class

public class OrderPizza {
    private AbsFactory factory;
    public OrderPizza(AbsFactory factory) {
        setFactory(factory);
    }
    private void setFactory(AbsFactory factory) {
        do {
            this.factory = factory;
            String orderType = getType();
            // Factory may be a factory subclass in Beijing or a factory subclass in London
            Pizza pizza = factory.createPizza(orderType);
            if (pizza == null) { 
                System.out.println("Subscription failed");
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }   
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

Analysis: Pizza pizza = factory createPizza(orderType); This statement mainly uses the polymorphism in the java foundation, and the specific implementation functions are handed over to the subclass that implements its interface

Step 4: create a run class

public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new LDFactory());
    }
}

Analysis: suppose you buy pizza in London

Operation results:

summary

1. Significance of factory mode:

The code of the instantiated object is extracted and put into a class for unified management and maintenance, so as to decouple the dependency relationship with the main project. So as to improve the expansibility and maintainability of the project.

2. Three factory patterns (simple factory pattern, factory method pattern and abstract factory pattern)

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
Do not let classes inherit concrete classes, but inherit abstract classes or implement interfaces
Do not override methods already implemented in the base class.

Topics: Java Algorithm