1, the preface

If we say that the factory method pattern is an upgrade of a simple factory, then the abstract factory method pattern in this article is an upgrade of the factory method pattern: using inheritance implementation, delegate object creation to subclasses (defer object instantiation to subclasses), Subclassing the factory method to create objects "decouples" the "implementation" of the product from use, reducing the decoupling of the code. In this article, we'll talk about the abstract factory patternCopy the code

Portal: Simple Factory Portal: Factory method mode


2. Requirement scenarios and technical implementation

Abstract Factory Demo Demand scenario: chain brand restaurant X can cook 4 kinds of rice: Salad BBQ Rice - Recipe: BBQ Salad Sauce Rice Vegetable Salad Chicken Rice - Recipe: BBQ Salad Sauce Rice Vegetable Tomato BBQ Rice - Recipe: BBQ Tomato Tomato Chicken Rice - Recipe: Chicken Tomato sauce sauce Rice vegetable Due to the supply channels and regional differences, the production ingredients of franchised shop A and B are different: Franchised shop A: barbecue, chicken, salad sauce, tomato sauce, long-grain rice lettuce franchised shop B: barbecue, chicken, salad sauce, tomato sauce, round rice, cabbage, unified production steps for each meal: Analysis needs: The only difference between stores A and B is that the raw materials for each "rice" are different, and there is no difference in other production processes. Therefore, the changing parts are abstracted out: raw materials of A and raw materials of B (raw material abstraction factory A and B) technology implementation: Creating abstract "rice" class, has the preparation, cooking, boxed packaging method Each "rice" class "rice" abstract class, rewrite the various steps of implementation (according to different formula, the ingredients of the raw material) for each meal although the making craft of the same, but the use of the raw materials of A shop and B store is different, so the four "rice" rice don't rely on different materials Create an abstract raw material interface. Each method is used to create the raw material (dependent on the abstract raw material) to implement the abstract raw material interface (abstract factory interface), and create the raw material class A and raw material class B (concrete factory) to specify the concrete raw material used by each raw material According to the category of raw materials, make abstract product classes (for the abstract factory to rely on) and concrete product classes (for the specific factory to rely on), create abstract store classes, generate a meal object, and control the production process (prepare food materials -> process cooking -> box placing -> package delivery). Inherit from the abstract store class, create specific store A and store B, rewrite the process of creating specific rice, and rely on raw material factory A and factory B respectively, to achieve the purpose of different stores, the same production process, the same rice using different raw materialsCopy the code

The code implementation process described above may seem confusing, but let’s compare the code

Because engineering class is more, for logical clarity, paste a code structure diagram

Structure Description: com.brave.food.abstractfactory.ingredient.meat com.brave.food.abstractfactory.ingredient.rice Com. Brave. Food. Abstractfactory. Ingredient. Sauce com. Brave. Food. Abstractfactory. Ingredient. The veggies above four package for all 6 kinds of raw materials Com. Brave. Food. Abstractfactory. Factory RiceIngredientFactory class as abstract factory interface of raw material, provides for each of the raw materials to create RiceIngredientFactoryA and RiceIngredientFactoryB are the concrete raw material factories A and B that implement the abstract raw material factory interface. Control each instantiation of the raw materials of the specific raw material which is a com. Brave. Food. Abstractfactory. Rice package food such as meals superclass that contains the attribute of rice (name + 6 kinds of raw materials), and methods (preparation, cooking, packaging) The remaining four classes inherit from the Food, depend on the specific raw material factory to create different ingredients, rewrite the preparation material combination com. Brave. Food. Abstractfactory. Store RiceStore for store abstract class, specifies the rice of the same production process, and will create specific meals delays to subclasses for different shops depend on different raw materials factory, achieve the same rice using different raw materials com. Brave. Food. Abstractfactory. Client test classCopy the code

3. Abstract factory pattern

Abstract Factory: The abstract factory pattern provides a solution for product families by providing an interface to create a family of related or interdependent objects without specifying concrete classesCopy the code

1) Simple description of raw materials (there are many categories and little significance, unified description)

/ / raw materials are divided into four kinds of 6 kinds of stored in com. Brave. Food. Abstractfactory. Ingredient. * under protected Sauce salad; // Protected Sauce tomato; // Protected Meat barbecue; // protect Meat chicken; // Chicken protected Rice; // Rice protected Veggies; 1. What's in them? 2, What's in them? 2, What's in them A TomatoSauceA TomatoSauceB TomatoSauceB Rice, Veggies, round grain Rice, Veggies, Cabbage, LettuceCopy the code

2) Raw material abstract factory

Public interface RiceIngredientFactory {public Meat createChicken(); public Meat createBarbecue(); public Sauce createTomato(); public Sauce createSalad(); public Rice createRice(); public Veggies createVeggies(); }Copy the code

3) Specific raw material factory A

The concrete raw material factories A and B that implement the abstract raw material factory interface control which concrete raw material is instantiated for each raw materialCopy the code
package com.brave.food.abstractfactory.factory; import com.brave.food.abstractfactory.ingredient.meat.BarbecueA; import com.brave.food.abstractfactory.ingredient.meat.ChickenA; import com.brave.food.abstractfactory.ingredient.meat.Meat; import com.brave.food.abstractfactory.ingredient.rice.LongGrainRice; import com.brave.food.abstractfactory.ingredient.rice.Rice; import com.brave.food.abstractfactory.ingredient.sauce.SaladSauceA; import com.brave.food.abstractfactory.ingredient.sauce.Sauce; import com.brave.food.abstractfactory.ingredient.sauce.TomatoSauceA; import com.brave.food.abstractfactory.ingredient.veggies.Lettuce; import com.brave.food.abstractfactory.ingredient.veggies.Veggies; /** * A ingredients factory ** roast A * chicken A * tomato sauce A * long rice ** @author ** / public class RiceIngredientFactoryA foods ** RiceIngredientFactory { @Override public Meat createChicken() { Meat chicken = new ChickenA(); return chicken; } @Override public Meat createBarbecue() { Meat barbecue = new BarbecueA(); return barbecue; } @Override public Sauce createTomato() { Sauce sauce = new TomatoSauceA(); return sauce; } @Override public Sauce createSalad() { Sauce sauce = new SaladSauceA(); return sauce; } @Override public Rice createRice() { Rice rice = new LongGrainRice(); return rice; } @Override public Veggies createVeggies() { Veggies veggies = new Lettuce(); return veggies; }}Copy the code

4) Create a unified abstract class for rice

package com.brave.food.abstractfactory.rice; import com.brave.food.abstractfactory.ingredient.meat.Meat; import com.brave.food.abstractfactory.ingredient.rice.Rice; import com.brave.food.abstractfactory.ingredient.sauce.Sauce; import com.brave.food.abstractfactory.ingredient.veggies.Veggies; ** @author ** / public abstract class Food {private String name; // name protected Sauce salad; // Protected Sauce tomato; // Protected Meat barbecue; // protect Meat chicken; // Chicken protected Rice; // Rice protected Veggies; Public void prepare(); Public void cook(){system.out.println (" food-cook "); } public void pack(){system.out.println (" food-pack ");} public void pack(){system.out.println (" food-pack "); } public String getName() { return name; } public void setName(String name) { this.name = name; } public void print() { if(name ! =null ){ System.out.println("name = " + name); } if(salad ! =null ){ System.out.println("salad = " + salad.toString()); } if(tomato ! =null ){ System.out.println("tomato = " + tomato.toString()); } if(chicken ! =null ){ System.out.println("chicken = " + chicken.toString()); } if(barbecue ! =null ){ System.out.println("barbecue = " + barbecue.toString()); } if(rice ! =null ){ System.out.println("rice = " + rice.toString()); } if(veggies ! =null ){ System.out.println("veggies = " + veggies.toString()); }}}Copy the code

5) Specific meals (take one of them as an example)

Each "rice" class inherits the abstract "rice" class and rewrites the realization of each step (different ingredients are needed according to different recipes). Although the production process of each rice is the same, the raw materials used by shop A and shop B are different, so the four "rice" classes do not rely on different raw materialsCopy the code
package com.brave.food.abstractfactory.rice; import com.brave.food.abstractfactory.factory.RiceIngredientFactory; /** * Salad BBQ rice ** Recipe: BBQ salad sauce RICE vegetable ** Factory A: BBQ salad sauce A long grain rice lettuce ** Factory B: BBQ SALAD sauce B round grain rice cabbage ** Inherit from the abstract class of the meal and re-write the process of preparing ** @author Brave * */ public class SaladBarbecueRice extends Food { RiceIngredientFactory ingredientFactory; public SaladBarbecueRice(RiceIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } @Override public void prepare() { System.out.println("Preparing " + getName()); barbecue = ingredientFactory.createBarbecue(); salad = ingredientFactory.createSalad(); rice = ingredientFactory.createRice(); veggies = ingredientFactory.createVeggies(); this.print(); }}Copy the code

6) Create store abstract class (Factory method pattern)

RiceStore is a store abstract class that specifies the same process for making meals and postpones the creation of specific meals to subclassesCopy the code
package com.brave.food.abstractfactory.store; import com.brave.food.abstractfactory.rice.Food; /** * The restaurant abstract class ** controls the uniform production process ** delays the creation of concrete objects to subclasses ** @author Brave ** / public abstract class RiceStore {public Food orderRice(String type){ Food rice; rice = this.createRice(type); System.out.println("-----------"+type+"----------"); rice.prepare(); rice.cook(); rice.pack(); return rice; } protected abstract Food createRice(String type); }Copy the code

7) Create specific stores

RiceStoreA and RiceStoreB have rewritten the production methods of various rice for the specific stores inherited from RiceStore. Different stores rely on different raw material factories to achieve the same rice using different raw materialsCopy the code
package com.brave.food.abstractfactory.store; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import com.brave.food.abstractfactory.factory.RiceIngredientFactory; import com.brave.food.abstractfactory.factory.RiceIngredientFactoryA; import com.brave.food.abstractfactory.rice.Food; import com.brave.food.abstractfactory.rice.SaladBarbecueRice; import com.brave.food.abstractfactory.rice.SaladChickenRice; import com.brave.food.abstractfactory.rice.TomatoBarbecueRice; import com.brave.food.abstractfactory.rice.TomatoChickenRice; /** * public RiceStoreA extends RiceStore {@override protected Food ** @author Brave ** / public RiceStoreA extends RiceStore {@override protected Food createRice(String type) { Food rice = null; RiceIngredientFactory ingredientFactory = new RiceIngredientFactoryA(); if(type.equals("TomatoBarbecue")){ rice = new TomatoBarbecueRice(ingredientFactory); rice.setName("TomatoBarbecueRice"); } else if(type.equals("TomatoChicken")){ rice = new TomatoChickenRice(ingredientFactory); rice.setName("TomatoChickenRice"); } else if(type.equals("SaladChicken")){ rice = new SaladChickenRice(ingredientFactory); rice.setName("SaladChickenRice"); } else if(type.equals("SaladBarbecue")){ rice = new SaladBarbecueRice(ingredientFactory); rice.setName("SaladBarbecueRice"); } // try { // Constructor c = null; // Class clazz = null; // try { // clazz = Class.forName(TomatoBarbecueRice.class.getName()); } catch (ClassNotFoundException e) {// system.out.println (" class does not exist "); // e.printStackTrace(); // } // c = clazz.getConstructor(RiceIngredientFactory.class); // try { // Food rice1 = (Food) c.newInstance(ingredientFactory); // rice1.setName("SaladBarbecueRice"); } catch (InstantiationException e) {// system.out.println (" abstract classes or interfaces are not supported "); // e.printStackTrace(); } catch (IllegalAccessException e) {// system.out.println (" there is not enough permission to access private objects "); // e.printStackTrace(); // } catch (IllegalArgumentException e) { // e.printStackTrace(); // } catch (InvocationTargetException e) { // e.printStackTrace(); // } // } catch (NoSuchMethodException e) { // e.printStackTrace(); // } catch (SecurityException e) { // e.printStackTrace(); // } finally { // // } return rice; }}Copy the code

8) test class

package com.brave.food.abstractfactory;

import com.brave.food.abstractfactory.store.RiceStore;
import com.brave.food.abstractfactory.store.RiceStoreA;
import com.brave.food.abstractfactory.store.RiceStoreB;

public class Client {

    public static void main(String[] args) {

        // Store A and store B
        RiceStore riceStoreA = new RiceStoreA();
        RiceStore riceStoreB = new RiceStoreB();

        // Roast rice with tomato
// riceStoreA.orderRice("TomatoBarbecue");
// riceStoreB.orderRice("TomatoBarbecue");

        // Tomato, chicken and rice
// riceStoreA.orderRice("TomatoChicken");
// riceStoreB.orderRice("TomatoChicken");

        // Salad risotto
// riceStoreA.orderRice("SaladChicken");
// riceStoreB.orderRice("SaladChicken");

        // Salad chicken rice
        riceStoreA.orderRice("SaladBarbecue");
        riceStoreB.orderRice("SaladBarbecue"); }}Copy the code

Printout:

-----------SaladBarbecue---------- Preparing saladrubs for barbecuerice name = Saladrubs for ice salad = A barbecue = A Rice = veggies = lettuce Food-cook Food-pack -----------SaladBarbecue---------- Preparing saladrubs name = A) barbecue B) rice = veggies = order = order = order = order = orderCopy the code

As can be seen from the Log output, store A and STORE B produce the same rice with the same production process and different raw materials


4. Summary of advantages and disadvantages of abstract factory model

Raw material Factory interface (Abstract Factory interface: provides interfaces for product families) Produces various raw material abstract factories: Provides an interface to create a family of related or interdependent objects without specifying concrete classes Abstract factory pattern provides solutions for product families Abstract factory scenarios: 1, a series to create independent of his products, combination and said 2, a system should be composed of multiple products in one configuration 3, when want to emphasize the design of a set of related product objects for a combination of 4, as to provide a product class libraries, and just show their interfaces rather than implementation advantages: The client often need to switch configuration (exchange product series), the client through an example of abstract interface to manipulate specific class name will not appear in the client abstract factory allows customers to use the abstract interface to create a set of related products, and don't need to care about what your product is real output, make customer decoupling faults from specific products: Abstract factory mode to facilitate the exchange of product family, and then change the declared product class place to make a large number of modifications solution: Add if,Switch branch judgments, or reflection (the implementation of reflection is included in the comments section of the code) to create product families by abstracting the interface provided by the factory, decoupling the code from the actual factory, implementing various factories in different contexts, and making different products Because the code is decoupled from the actual production, we can substitute different factories for different behaviorsCopy the code

5. Three factory modes

1) Comparison of the three modes

Simple Factory: While not really a design pattern, it is a simple way to enhance code portability by removing client-side and product-specific dependencies: Using an inheritance implementation, which delegates object creation to subclasses (postponing object instantiation to subclasses), subclasses implement factory methods to create object clients only need to know the abstract type being used, do not care about the concrete type factory method pattern, and are only responsible for decoupling the client from the concrete type abstract factory pattern: Using object composition implementation, the creation of the object is implemented in the method exposed by the factory interface to define the abstract type of the creation of the product family, the method produced by the product is defined by the subclass, and the abstract factory pattern is passed into the code using the abstract type after instantiation, decoupling the client from the actual concrete product usedCopy the code

2) Advantages and disadvantages of the three modes

Simple Factory - Advantages: Eliminates client and specific product dependencies, enhances code portability Simple Factory - Disadvantages: Violates the open-closed principle and is troublesome to add new products factory method pattern - Advantages: Using inheritance, create entrusted to a subclass of the object (the object instantiated delay to subclass), subclass implementation factory method to create the object The client just need to know the type of the abstract, don't care about the specific types of factory method pattern, only responsible for the client from a specific type of decoupling is compatible to the backend module open closed principle Factory method pattern - Disadvantages: Adding new products requires the creation of a large number of new classes. The client part still violates the open-closed principle, but the back-end judgment logic is moved to the foreground abstract factory pattern - Advantages: Implementation using a combination of object, the object of creating implementation defined in the interface exposed by the factory method to create product family of an abstract type, defined by the subclass product produced by the method, instantiated to use an abstract type in the code the abstract factory pattern, the client is decoupled from the actual concrete products abstract factory pattern - faults: When new products are added to the abstract factory interface, new products need to be added, and all the classes that implement this interface need to be expandedCopy the code

3) Common ground of factory model:

(1) Encapsulate the object creation process; (2) reduce the dependency between the client and the concrete class, and promote loose coupling; (3) comply with the principle of dependency inversion, so that high-level components cannot depend on low-level components, and depend on abstraction rather than concreteCopy the code