This is the 10th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Raw material supply problem

The factory method model provides us with a way to use the factory to obtain different kinds of products, and solves the problem of too much pressure on the factory at the branch. Now, we need different ingredients for different pizzastores. If we continue to use the factory method pattern then we might write something like this.

abstract class Dough { abstract String name; } class ThickCrustDough extends Dough { // ... } class ThinCrustDough extends Dough { // ... } abstract class DoughFactory extends IngredientFactory { Dough createDough(String type) { if(...) { return new ThickCrustDough(); } else if(...) { return new //... } } } abstract class SauceFactory extends IngredientFactory { //... } / /...Copy the code

When we have more and more raw materials, it will become complicated to use the factory method mode to write. We need to define factories of various raw materials, and they have complex inheritance relationship with the raw material factory, which greatly improves the coupling of the whole program. The complex inheritance relationship is not conducive to the maintenance of the following code. It also violates an important principle — open to extension, closed to modification. To solve this problem, we need to replace complex inheritance with composition. We find that every place provides ingredientfactories with local characteristics to supply Pizza stores. We can build ingredientfactories in different regions to provide different ingredientfactories. This is the abstract factory pattern — providing an interface for creating families of related or dependent objects without specifying concrete classes. This factory class is abstract, hence the name Abstract Factory pattern. The first is a Pizza concrete class to specify Pizza composition and operation.

public abstract class Pizza { private String name; public Dough dough; public Sauce sauce; Veggies veggies[]; Cheese cheese; Clams clams; abstract void prepare(); void bake() { System.out.println("bake"); } void cut() { System.out.println("cut"); } void box() { System.out.println("box"); } void setName(String name) { this.name = name; } String getName() { return this.name; }}Copy the code

Then we define the ingredients

abstract class Dough { abstract String getName(); } abstract class Cheese { abstract String getName(); } abstract class Sauce { abstract String getName(); } abstract class Clams { abstract String getName(); } abstract class Veggies {} class Veggies extends Dough {@override String getName() {return "ThickCrustDough"; } } class ThinCrustDough extends Dough { @Override String getName() { return "ThinCrustDough"; } } class MarinaraSauce extends Sauce { @Override String getName() { return "MarinaraSauce"; } } class PlumTomatoSauce extends Sauce { @Override String getName() { return "PlumTomatoSauce"; } } class MozzarellaCheese extends Cheese { String getName() { return "MozzarellaCheese"; } } class FeshClams extends Clams { @Override String getName() { return "FleshClams"; } } class FrozenClams extends Clams { @Override String getName() { return "FrozenClams"; } } class Onion extends Veggies { //... } class Garlic extends Veggies { //... }Copy the code

We then define an abstract class for the raw material factory as a unified interface to invoke the factory.

public interface IngredientFactory {
    Dough createDough();
    Sauce createSauce();
    Cheese createCheese();
    Veggies[] createVeggies();
    Clams createClams();
}
Copy the code

Then there are specific classes of local factories

Public class ChicagoIngredientFactory IngredientFactory {@override public Dough createDough() { return new ThickCrustDough(); } @Override public Sauce createSauce() { return new PlumTomatoSauce(); } @Override public Cheese createCheese() { return new MozzarellaCheese(); } @Override public Veggies[] createVeggies() { return new Veggies[]{new Garlic(), new Mushroom()}; } @Override public Clams createClams() { return new FrozenClams(); Public class NewYorkIngredientFactory implements IngredientFactory {@override public Dough createDough() { return new ThinCrustDough(); } @Override public Sauce createSauce() { return new MarinaraSauce(); } @Override public Cheese createCheese() { return new ReggianoCheese(); } @Override public Veggies[] createVeggies() { return new Veggies[]{new Garlic(), new Onion(), new Mushroom()}; } public Clams createClams() { return new FeshClams(); }}Copy the code

PizzaStore abstract class for processing pizza orders and getting information

abstract class PizzaStore {

    void orderPizza(String type) {
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        System.out.println(this.getInfo(pizza));
    }

    private String getInfo(Pizza pizza) {
        return "Ingredients are " + pizza.dough.getName() + ", " + pizza.sauce.getName();
    }

    protected abstract Pizza createPizza(String type);
}
Copy the code

Below are the concrete classes of PizzaStore, the concrete implementers who make different kinds of pizzas and get the ingredients from the raw material factory in the and constructor.

//ChicagoPizzaStore public class ChicagoPizzaStore extends PizzaStore { private Pizza pizza = null; @Override protected Pizza createPizza(String item) { IngredientFactory pizzaIngredientFactory = new ChicagoIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(pizzaIngredientFactory); pizza.setName("Chicago style cheese Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(pizzaIngredientFactory); pizza.setName("Chicago style clam Pizza"); } return pizza; } } //NewYorkPizzaStore class NewYorkPizzaStore extends PizzaStore { private Pizza pizza = null; protected Pizza createPizza(String item) { IngredientFactory pizzaIngredientFactory = new NewYorkIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(pizzaIngredientFactory); pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(pizzaIngredientFactory); pizza.setName("New York Style Clam Pizza"); } return pizza; }}Copy the code

Test our PizzaStore

public class AbstractFactoryTest { public static void main(String[] args) { NewYorkPizzaStore newYorkPizzaStore = new NewYorkPizzaStore(); ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore(); newYorkPizzaStore.orderPizza("cheese"); System.out.println("----next-----"); newYorkPizzaStore.orderPizza("clam"); System.out.println("----next-----"); chicagoPizzaStore.orderPizza("cheese"); System.out.println("----next-----"); chicagoPizzaStore.orderPizza("clam"); System.out.println("----next-----"); }}Copy the code

Get the end result

The model structure

AbstractFactory: AbstractFactory, a unified interface for creating product class calls

ConcreteFactory: ConcreteFactory that provides desired concrete objects

AbstractProduct: AbstractProduct

Product: Specific Product

Pattern analysis

advantages

Instead of the factory approach creating products with an abstract interface and using concrete class decisions to instantiate which concrete class, the Abstract factory pattern provides an abstract interface to create a family of products, with each concrete factory responsible for creating concrete product methods. In addition, unlike the inheritance of the factory method, the abstract factory pattern creates a collection of objects through composition.

The abstract factory pattern isolates the generation of concrete classes so that customers do not need to know what is being created. Because of this isolation, it is relatively easy to change a specific plant. All concrete factories implement the common interfaces defined in the abstract factory, so simply changing an instance of the concrete factory can change the behavior of the entire software system to some extent. In addition, abstract factory pattern can achieve high cohesion and low coupling design purpose, so abstract factory pattern has been widely used.

When multiple objects in a product family are designed to work together, it ensures that clients always use only objects in the same product family. This is a very useful design pattern for software systems that need to determine their behavior based on the current environment.

It is convenient to add new specific factories and product families without modifying existing systems, in accordance with the “open closed principle”.

disadvantages

In object, add new products to extend the abstract factory to produce new kinds of products, all this is because the rules in the role of the abstract factory can be created product collection, new kinds of products to support means extensions to the interface, and this will involve the role of the abstract factory and all its subclasses change, obviously brings great inconvenience.

The inclination of the open and close principle (easy to add new factories and product families, troublesome to add new product grade structures).

Application of the abstract factory pattern

  • There is more than one product family in the system and only one of them is used at a time.
  • Products belonging to the same product family will be used together, and this constraint must be reflected in the design of the system.
  • The system provides a library of product classes, all of which appear in the same interface, so that the client is implementation-independent.

The main function of factory mode as a creation mode is to provide different instantiation objects under the premise of high cohesion and low coupling, which makes the code structure more hierarchical and convenient for later expansion. In actual situations, different factory mode can be selected according to the needs.