preface

  • Why choose this mode?
    • Practical, many scenarios can be regarded as “factory products” “factory class” model, so many times can be used;
  • In this paper,
    • This paper refers a lot to the book “Head First Design Pattern”. Taking the production process of pizza as an example, it starts from simple and original code and introduces the factory method pattern and abstract factory pattern to solve the problem of requirements by constantly adding requirements, which conforms to cognitive common sense and is easy to understand.

Common code problems

  • Background: a new pizzeria has opened somewhere in the United States, serving both cheese and Greek pizzas and accepting online orders

  • Let’s start by ordering Pizza

    public class PizzaStore{
      public Pizza orderPizza(String type){
        Pizza pizza;
        // Easy to change
        if (type.equals("cheese"){
          pizza = new CheesePizza();
        } else if((type.equals("greek")){
          pizza = new GreekPizza();
        } else{
          pizza = new NormalPizza();
        }
        returnpizza; }}Copy the code
    • Use implementations instead of interfaces;
    • Because of serious coupling, PizzaStore relies on all types of pizza. When adding new products or modifying products, the places created need to be modified, which violates the open and close principle, that is, orderPizza cannot be closed for modification. Not conducive to maintenance;
    • What went wrong?
      • The problem is “change”, the pressure comes from adding more pizza types;
    • How to modify?
      • “Identify the changes and separate them from the ones that don’t change.”

The appearance of the simple foreman

  • Encapsulate the code that creates the object: move the changes to another class that creates the pizza only, calling this new class a “factory”;

    public class SimplePizzaFactory{
    
    	public Pizza createPizza(String type){
        Pizza pizza = null;
        if (type.equals("cheese"){
          pizza = new CheesePizza();
        } else if((type.equals("greek")){
          pizza = new GreekPizza();
        } else if(type.equals("clam")){
          pizza = new ClamPizza();
        } else{
          pizza = new NormalPizza();
        }
        pizza.bake();
      	pizza.cut();
        pizza.box();
        returnpizza; }}Copy the code
    • In this way, orderPizza no longer cares about the specific Pizza type, but only about getting a Pizza from the factory;

      public class PizzaStore{
        
      	private SimplePizzaFactory factory;
        
      	public PizzaStore(SimplePizzaFactory factory){
          this.factory = factory;
        }
        
        public Pizza orderPizza(String type){
      		Pizza pizza = factory.createPizza(type);
      		returnpizza; }}Copy the code
    • And any place that wants to create a pizza can use the factory to do so, improving reusability;

    • Later, when requirements change, just modify this class;

    Simple factories are not incorporated into design patterns, but are more of a programming habit.

Factory method introduction

  • New demand: Pizza is so popular and fast growing that the owner plans to start the franchise model, but considering the different tastes of people in each region, each franchise may need to offer different flavors of pizza, such as New York, Chicago, California.

  • Method 1: use the existing structure to achieve

    NYPizzaFactory nyFactory = new NYPizzaFactory ();
    PizzaStore nyStore = new PizzaStore(nyFactory);
    nyStore.orderPizza("Veggie");
    Copy the code
    • Cons: Although both are franchisees, their factories are different, and the process of making pizza (baking, slicing, packaging) becomes uncontrollable;
  • Method two: use factory method to achieve

    • Join in the restriction process for pizza shops and restrict franchised shops
     public abstract class PizzaStore{
       
       public Pizza orderPizza(String type){
         Pizza pizza;
         pizza = createPizza(type);
         pizza.bake();
         pizza.cut();
         pizza.box();
         return pizza;
       }
       
       protected abstract Pizza createPizza(String type);
     }
    Copy the code
    • The production process was transferred from the factory to PizzaStore, so as to limit the franchise stores;

    • Class was changed to abstract, and the abstract method createPizza was added, so that the subclasses (NYPizzaStore, ChicagoPizzaStore) could only produce pizza with characteristic flavor, and no other processing was carried out.

    • The production object changed from factory to abstract method, which is the origin of factory method;

    • Take a look at the booking process under this method:

      PizzaStore nyPizzaStore = new NYPizzaStore();
      nyPizzaStore.orderPizza("cheese");
      Copy the code
  • contrast

    • All franchisees can achieve the same processing of orderPizza;
    • Allow subclasses to make decisions: The core of franchising is to allow the franchise to make decisions, not the “factory”
    • Focus on the essence of the problem. Users only focus on the storefront and pizza taste when ordering, not the factory
    • A simple factory does everything in one place, whereas a factory method creates a framework and lets subclasses decide how to implement it.

    The core issue remains “change”, trying to isolate only the areas that are easy to change (making pizza and expanding pizza varieties), while unifying the areas that don’t change often (baking, slicing, packaging).

  • One thing to add: the pizza itself

    • As mentioned above, different franchisees can create different flavors of pizza, which shows that pizza itself also has similarities and differences, which is also a kind of inheritance relationship. Examples of base classes and inheritance relationships are as follows:
     public class Pizza{
     	String name;
     	float size;
     	float thickness;
     }
    Copy the code

Factory Method Definition

  • Definition: The factory method pattern defines an interface for creating objects, but it is up to subclasses to decide which class to instantiate. The factory method lets classes defer instantiation to subclasses.
  • Explanation:
    • Interface to create objects: Abstract methods, such as createPizza
    • Subclasses decide which classes to instantiate: When writing creators, you don’t need to know which products are actually being created. Which subclasses are selected determines which products are being created.
    • The creator is a class that implements all the methods for manipulating the product, but not the factory methods;
  • UML
  • The onCreate method in the Activity is actually the use of the factory method

Abstract Factory introduction

  • Make sure the ingredients are consistent

    • There are new demands: some franchisees use low-priced raw materials to increase profits, and measures need to be taken to ensure the consistency of raw materials so as not to damage the company’s reputation.
    • A more intuitive approach is to build a factory to produce the ingredients and ship them to the franchise stores. However, while all franchisees need the same kinds of ingredients, each specific ingredient is different. For example, red sauce in New York is not the same as red sauce in Chicago. Also, using local ingredients not only eliminates shipping costs but is more in line with local needs, so it would be better for the “headquarters” to define just one set of ingredients and let the regional processing plants produce each one.
  • Build a raw material factory

     public interface PizzaIngredientFactory{
       public Dough createDough(a);/ / the dough
       public Sauce createSauce(a);/ / sauce
       public Clams createCheese(a);/ / cheese
     }
    Copy the code
    • To build a factory for each region, a subclass derived from PizzaIngredientFactory is created to implement each creation method.

    • Finally it all came together, integrating the new raw material factory into the old PizzaStore code.

    • For example, creating a New York raw material factory

     public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
     	public Dough createDough(a){
     		return new ThinCrustDough();
     	}
       
     	public Sauce createSauce(a){
     		return newMatrinaraSauce(); }... }Copy the code
    • New York versions are available for each ingredient in the ingredient family.
    • Other areas are similar, omitted.
  • Redo a pizza

    • The Pizza class was mentioned above. Here we add some ingredients to the Pizza class.
     public abstract class Pizza{
     	String name;
     	float size;
     	float thickness;;
       Dough dough;
       Sauce sauce;
       Cheese cheese;
       
       abstract void prepare(a);
     }
    Copy the code
    • Next, create a New York-style pizza, and notice that from now on, franchisees will have to get the ingredients directly from the factory, so no more cutting corners.
     public class CheesePizza extends Pizza{
       PizzaIngredientFactory ingredientFactory;
       
       public CheesePizza(PizzaIngredientFactory ingredientFactory){
         this.ingredientFactory = ingredientFactory;
       }
       
       @over
       void prepare(a){ dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); clam = ingredientFactory.createCheese(); }}Copy the code
  • So what did we do

    • New types of factories, known as abstract factories, were introduced to create families of raw materials.
    • Product families can be created by abstracting the interfaces provided by the factory. A variety of factories can create a variety of products.
    • Their relationship is as follows

Abstract Factory definition

  • Definition: The Abstract factory pattern provides an interface for creating families of related or dependent objects without explicitly specifying concrete classes.
  • Explanation:
    • Similar to the factory approach, the subclass determines which product to create, decoupling it from the specific product
    • You create a set of products (object families) rather than individual products. Each factory has a different implementation for this product family.
  • UML

Factory method vs. abstract factory

  • Factory methods generally produce only one kind of product, the core is to define abstract methods, through the subclass to create concrete products, different subclasses to produce different products; Abstract factories produce a set of products, each factory subclass needs to produce a set of products, and each product has a different concrete type in each subclass;
  • Factory methods lurk in abstract factories. This makes sense, because the task of an abstract factory is to define an interface responsible for creating a set of products, and each method of that interface is responsible for creating a concrete product, so let a subclass of the factory implement those methods. For example, going back to the pizza factory, if we look at just one ingredient: dough, the factory interface defines a method to createDough called createDough(), and the subclass implements this method to create different doughs, i.e., “the subclass decides which class to instantiate”, which is exactly what the factory method defines.
  • A factory method has only one abstract method, and an abstract factory has one large interface (or multiple abstract methods);
  • Thing in common:
    • Dependency inversion principle: both depend on abstractions, but not on concrete classes;
    • Class explosion: Abstract factories, in particular, increase classes significantly;
  • Applicable scenarios:
    • Abstract factory: This is useful when you need to create a family of products and when you want to bring together related products to be manufactured.
    • Factory method: When creating a product, you can either decouple your client code from specific classes that need to be instantiated, or you don’t yet know which classes will need to be instantiated in the future.

Compare the factory method with the template method

  • A parent class defines an abstract interface for subclasses to implement, which looks a bit like a template method, but there are differences:

    • A subclass of the factory method is to produce different products (ProductA,ProductB) that have a common parent (Product)
    • The purpose of the factory method is to decouple the caller (Client) from the concrete product
  • The above two points show the meaning and function of the factory, while the template method focuses on the parent class encapsulating the main framework of some logic and letting the child class handle some steps.

  • Addendum: Template method definition: a framework for defining an algorithm in an operation, while deferring some steps to subclasses. Allows subclasses to redefine specific steps of an algorithm without changing its structure.

Actual combat scene

  • Daily development, can put a page creation process as products factory production process, many pages are related with the state, such as the login state and not login state, has the drop, and drop out of the state, it is equivalent to the different products, you can use the factory method pattern, will delay under different status page to subclass initialization;
  • If you split the page up into header, in-page, and footer sections, each of which is related to state, you can use an abstract factory to define interfaces for creating header, in-page, and footer sections, leaving it up to subclasses to create specific instances in what state.

Personal feeling

  • Objects are always created. We are always dealing with “change”, and we can create objects directly by reference where there is no change. Identify the changes and separate them from the ones that don’t change.”
  • Design mode is a kind of thinking mode, which does not need to stick to specific framework and strict definition. Its guiding principles are object-oriented design principles.

Article by Panxc