6. Abstract factory pattern

6. Abstract factory pattern

1 Introduction

If we say that the factory method pattern is an upgrade of a simple factory, then the abstract factory pattern to be discussed in this article is an upgrade of the factory method pattern Factory method pattern: Use inheritance to delegate the creation of objects to subclasses (delay the instantiation of objects to subclasses), Implement factory methods to create objects through subclasses, "decouple" the "implementation" of the product from use, and reduce the degree of code coupling In this article, we will talk about the abstract factory pattern, and the factory method pattern will continue to be used in the code. Examples of takeaway fast food restaurants Copy code

Portal: Simple Factory
Portal: Factory Method Pattern


2. Demand scenarios and technical realization

Abstract factory Demo demand scenario: Chain brand restaurant X can cook 4 kinds of rice: Salad Barbecue Rice-Recipe: Barbecue Salad Sauce, Rice, Vegetables Salad Chicken Rice-Recipe: Chicken Salad Sauce, Rice, Vegetables Tomato Roasted Rice-Recipe: Roasted Rice with Tomato Sauce and Vegetables Tomato Chicken Rice-Recipe: Chicken Tomato Sauce, Rice and Vegetables Due to the differences in supply channels and regions, the ingredients for franchise store A and store B are different: Franchise A: Grilled Meat A Chicken A Salad Sauce A Tomato Sauce A Long Grain Rice Lettuce Franchise shop B: barbecue B chicken B salad dressing B tomato sauce B round rice cabbage The uniform production steps for each meal: Prepare ingredients->Processing and cooking->Packing in boxes Analyze requirements: The only difference between shop A and B is that the raw materials used to make each "rice" are different, and there is no difference in other production processes. So abstract the changed parts: A's raw material class, and B's raw material class (raw material abstract factory A and B) Technical realization: Create an abstract "rice" class with methods for preparing, cooking, boxing and packing Each "rice" class inherits the abstract "rice" class and rewrites the realization of each step (depending on the recipe, the ingredients needed are different) Although the production process of each type of rice is the same, the raw materials used in shop A and shop B are different, so do not rely on different raw materials for the four kinds of "rice" rice. Create an abstract raw material interface, and each method is used to create raw materials (depending on abstract raw materials) Implement the abstract raw material interface (abstract factory interface), create raw material A and raw material B (concrete factory) to specify the specific raw materials used by each raw material Make abstract products (for abstract factories to rely on) and specific product categories (for specific factories to rely on) according to the types of raw materials Create an abstract store class, generate a meal object, and control the production process (preparing ingredients -> processing and cooking -> boxing and placing -> packaging and delivery) Inherited from the shop abstract class, create concrete shop A and shop B, rewrite the process of creating concrete rice, and rely on raw material factory A and factory B respectively, Realize the purpose of different stores, the same production process, and the use of different raw materials for the same meal Copy code

The code implementation process mentioned above may seem confusing, let's compare the code below

Since there are many engineering categories, in order to make the logic clear, post 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.veggies All 6 raw materials in the above four packages com.brave.food.abstractfactory.factory The RiceIngredientFactory class is an abstract raw material factory interface, which specifies the creation of each raw material RiceIngredientFactoryA and RiceIngredientFactoryB are concrete raw material factories A and B that implement the abstract raw material factory interface. Control which specific raw material is instantiated for each raw material com.brave.food.abstractfactory.rice The Food category in the package is a meal super category, which contains the attributes of the rice (name + 6 raw materials), and methods (preparation, cooking, packaging) The remaining four classes inherit from the Food class, relying on specific raw material factories to create different ingredients, and rewrite the material combination in the preparation stage com.brave.food.abstractfactory.store RiceStore is an abstract class of stores, which prescribes the same production process of rice, and delays the creation of specific meals to sub-categories. Different stores rely on different raw material factories to achieve the same meal using different raw materials com.brave.food.abstractfactory.Client test class Copy code

3. Abstract factory pattern

Abstract factory: Provides an interface to create a series of related or interdependent object families without specifying specific classes The abstract factory model provides a solution for the product line Copy code

1) A simple description of the raw materials (there are many types and not much meaning, unified description)

//The raw materials are divided into 4 categories and 6 types and are stored under com.brave.food.abstractfactory.ingredient.* protected Sauce salad;//Salad sauce protected Sauce tomato;//tomato sauce protected Meat barbecue;//barbecue protected Meat chicken;//chicken protected Rice rice;//rice protected Veggies veggies;//vegetables Meat Barbecue A BarbecueA Barbecue B BarbecueB Chicken A Chicken A Chicken B Chicken B Sauce Salad Sauce A SaladSauceA Salad Sauce B SaladSauceB Tomato Sauce A TomatoSauceA Tomato Sauce B TomatoSauceB Rice Long Grain Rice LongGrainRice Round Grain Rice RoundedGrainRice Vegetable Veggies Cabbage Lettuce Lettuce Copy code

2) Abstract factory of raw materials

/** * Raw material factory interface * * Production of various raw materials * * @author Brave * */ public interface RiceIngredientFactory { public Meat createChicken(); public Meat createBarbecue(); public Sauce createTomato(); public Sauce createSalad(); public Rice createRice(); public Veggies createVeggies(); } Copy code

3) Specific raw material factory A

Implement particular abstract factory interface raw plant materials A and B, the control of each material specific examples of the raw material which is copy 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 raw material factory * * Barbecue A * Chicken A * Tomato sauce A * Long grain rice * Lettuce * * @author Brave * */ public class RiceIngredientFactoryA implements 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 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; /** * Food unified interface * Various Food implement this interface * Prescribes the production links and attributes of Food * * @author Brave * */ public abstract class Food { private String name;//name protected Sauce salad;//Salad sauce protected Sauce tomato;//tomato sauce protected Meat barbecue;//barbecue protected Meat chicken;//chicken protected Rice rice;//rice protected Veggies veggies;//vegetables //ready public abstract void prepare(); //cooking public void cook(){ System.out.println("Food-cook"); } //Bale 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 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 (depending on the recipe, the ingredients needed are different) Although the production process of each type of rice is the same, the raw materials used in shop A and shop B are different, so do not rely on different raw materials for the four kinds of "rice" rice. Copy code
package com.brave.food.abstractfactory.rice; import com.brave.food.abstractfactory.factory.RiceIngredientFactory; /** * Salad barbecue rice * * Recipe: grilled meat salad sauce, rice, vegetables * Factory A: Grilled Meat A, Salad Dressing A, Long Grain Rice and Lettuce * Factory B: Roast B, Salad Sauce B, Round Rice and Cabbage * * Inherited from the meal abstract class, overriding the preparation process prepare() * * @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 code

6) Create store abstract class (factory method pattern)

RiceStore to shop abstract class provides the same production process rice, and create specific meals deferred to subclasses copy the code
package com.brave.food.abstractfactory.store; import com.brave.food.abstractfactory.rice.Food; /** * Hotel abstract class * * Control the unified production process * Delay the creation of specific 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 code

7) Create a specific store

RiceStoreA and RiceStoreB are specific stores inherited from RiceStore Rewrite the methods of making various kinds of rice, different shops rely on different raw material factories to achieve the same rice using different raw materials Copy 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; /** * Hotel A * * Use A raw material factory to provide production materials * * @author Brave * */ public class 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("Insufficient permissions, that is, no access to 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 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) { //Shop A and Shop B RiceStore riceStoreA = new RiceStoreA(); RiceStore riceStoreB = new RiceStoreB(); //Tomato barbecue rice //riceStoreA.orderRice("TomatoBarbecue"); //riceStoreB.orderRice("TomatoBarbecue"); //Tomato Chicken Rice //riceStoreA.orderRice("TomatoChicken"); //riceStoreB.orderRice("TomatoChicken"); //Salad barbecue rice //riceStoreA.orderRice("SaladChicken"); //riceStoreB.orderRice("SaladChicken"); //Salad chicken rice riceStoreA.orderRice( "SaladBarbecue" ); riceStoreB.orderRice( "SaladBarbecue" ); } } Copy code

Printout:

-----------SaladBarbecue---------- Preparing SaladBarbecueRice name = SaladBarbecueRice salad = salad dressing A barbecue = barbecue A rice = long grain rice veggies = lettuce Food-cook Food-pack -----------SaladBarbecue---------- Preparing SaladBarbecueRice name = SaladBarbecueRice salad = salad dressing B barbecue = barbecue B rice = round rice veggies = cabbage Food-cook Food-pack Copy code

From the log output, it can be seen that stores A and B make the same rice, the production process is the same, and the raw materials used are different


4. Summary of abstract factory pattern + advantages and disadvantages

Raw material factory interface (abstract factory interface: provide interface for product family) Production of various raw materials Abstract factory: Provides an interface to create a series of related or interdependent object families without specifying specific classes The abstract factory model provides a solution for the product line Application scenarios of abstract factory: 1. A series should be independent of the creation, combination and presentation of its products 2. When a system is to be configured by one of multiple product series 3. When it is necessary to emphasize the design of a series of related product objects for joint use 4. When a product class library is to be provided, and only their interface is displayed instead of the implementation advantage: When the client often needs to switch the configuration (exchange product series), the client manipulates the instance through the abstract interface, and the specific class name does not appear on the client The abstract factory allows customers to use abstract interfaces to create a set of related products without worrying about the actual output of the specific product, so that the customer is decoupled from the specific product Disadvantages: The abstract factory model is convenient for exchanging product series, and at the same time, a large number of changes are made where the product category is declared Solution: Add if, Switch branch judgment or reflection (the implementation of reflection has been included in the comment part of the code) Create a product family through the interface provided by the abstract factory, decouple the code from the actual factory, implement various factories in different contexts, and manufacture different products Since the code is decoupled from the actual product, we can replace different factories to achieve different behaviors Copy code

5. 3.factory modes

1) Comparison of the three modes

Simple factory: Although not a real design pattern, it is a simple method Removed the dependence of the client and specific products, and enhanced code portability Factory method pattern: Use inheritance to delegate the creation of the object to the subclass (delay the instantiation of the object to the subclass), and the subclass implements the factory method to create the object The client only needs to know the abstract type used, and does not care about the concrete type The factory method pattern is only responsible for decoupling the client from the specific type Abstract factory pattern: Use object composition to realize the creation of objects in the methods exposed by the factory interface Define the abstract type that creates the product family, define the method generated by the product by the subclass, and pass it into the code that uses the abstract type after instantiation The abstract factory pattern decouples the client from the actual specific products used Copy code

2) The advantages and disadvantages of the three modes

Simple factory-advantages: Removed the dependence of the client and specific products, and enhanced code portability Simple Factory-Disadvantages: Violation of the open-closed principle Adding new products is troublesome Factory method pattern-advantages: Use inheritance to delegate the creation of the object to the subclass (delay the instantiation of the object to the subclass), and the subclass implements the factory method to create the object The client only needs to know the abstract type used, and does not care about the concrete type The factory method pattern is only responsible for decoupling the client from the specific type The background module conforms to the 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 background judgment logic is transferred to the foreground Abstract factory pattern-advantages: Use object composition to realize the creation of objects in the methods exposed by the factory interface Define the abstract type that creates the product family, define the method generated by the product by the subclass, and pass it into the code that uses the abstract type after instantiation The abstract factory pattern decouples the client from the actual specific products used Abstract factory pattern-disadvantages: When new products are added and new products need to be added to the abstract factory interface, all classes that implement this interface need to be expanded, and the workload is heavy. Copy code

3) Common points of the factory model:

1. Encapsulate the object creation process 2. Reduce the dependency between the client and the concrete class and promote loose coupling 3. In line with the principle of dependency inversion, high-level components cannot be allowed to depend on low-level components, and abstraction does not depend on concrete Copy code