This is the second day of my participation in the August Text Challenge.More challenges in August

Preface: As usual I started coding with the factory pattern example of the Head First design pattern. And by simple factory, factory model, abstract factory model evolution, summarized their similarities and differences.

Head First believes that the simple factory is not a design pattern, but a programming habit, but it doesn’t stop us from using it. Let’s take a look at the factory pattern.

1. Pizzeria

First of all, we need to open a pizza shop. We can quickly develop a pizza shop and the logic of ordering pizza when the business is not complicated

public Pizza OrderPizza() { Pizza pizza = new Pizza(); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; Public void Bake() {} public void Bake() {} public void Bake() {} public void Bake() {} public void Cut() { public void Box() { } }Copy the code

If we have more Pizza types, we may need to define Pizza as an abstract class and return different pizzas according to the Pizza types ordered in the Order. We abstract the Pizza and transform the Order.

public class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza=null; if (type == "cheese") { pizza = new CheesePizza(); } else if (type == "viggie") { pizza = new VeggiePizza(); } //else if ...... pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; Public void Bake() {} public void Bake() {} public void Bake() {} public void Bake() {} Public void Box() {}} public class VeggiePizza: Pizza {} public class VeggiePizza: Pizza {}Copy the code

At this point we’re probably thinking, if we add pizzas or remove pizzas then we’re going to change the pizzeria.

Design principles are open for extension, closed for modification. We need to encapsulate the changes that create the pizza. There’s a “factory” class for creating pizzas.

And static, so there is no need to instantiate objects, and follow the principle of non-implementation programming.

public class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza = SimplePizzaFactory.CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } } public static class SimplePizzaFactory { public static Pizza CreatePizza(string type) { Pizza pizza = null; if (type == "cheese") { pizza = new CheesePizza(); } else if (type == "viggie") { pizza = new VeggiePizza(); } return pizza; }}Copy the code

This simply encapsulates the creation of pizza as a simple factory (static factory). Simple factories can also be done without static classes, but simple factories are not a specific design pattern (it can sometimes be confused as “factory mode”), and are more of a programming habit. We encapsulate the changes in a local area and only modify the factory class when there are changes.

2. More pizza places

Now we are going to open more pizzerias, like USSytlePizzaStore, CHNSytlePizzaStore.

We can take the simple factory model, create two pizza factories with different styles, and then create two pizza shops with different styles, and use the corresponding pizza factory to obtain the pizza shops with different styles.

But the change for us is the pizza place. We want the structure or the process of the pizzeria to follow certain rules, just different styles of pizza. Now we have a better solution: the factory model.

Now how do we do that

public abstract class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza= CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } public abstract Pizza CreatePizza(string type); } public class USSytlePizzaStore : PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza = null; if (type == "cheese") { pizza = new USStyleCheesePizza(); } else if (type == "viggie") { pizza = new USStyleVeggiePizza(); } return pizza; } } public class CHNSytlePizzaStore : PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza = null; if (type == "cheese") { pizza = new CHNStyleCheesePizza(); } else if (type == "viggie") { pizza = new CHNStyleVeggiePizza(); } return pizza; }} public class USStyleCheesePizza: Pizza {} public class USStyleVeggiePizza: Pizza {} public class CHNStyleCheesePizza: Pizza {} public class CHNStyleVeggiePizza: Pizza {}Copy the code

From the implementation we can see that we changed the PizzaStore into an abstract class, and that different pizzerias inherit the abstract class to return their own different pizza styles.

After this design, when adding products, we only modify the pizza creation in the specific subclass pizzeria, without affecting the pizzeria process and the implementation of other pizzeria.

The factory method pattern defines an interface for creating objects. The subclass decides which class to instantiate. The factory method lets the class defer instantiation to the subclass.

The difference between a factory method and a simple factory: A subclass of a factory method looks a lot like a simple factory. Whereas a simple factory does everything in one place, the factory method creates a framework and lets subclasses decide how to implement it.

3. Different ingredients for pizza

Pizza shops with different styles have different styles of pizza, and the different styles of pizza are caused by different ingredients, so the part of the change of different styles of pizza is the material. \

We first build the raw materials and raw materials factory, take the Chinese pizza raw materials factory as an example

Public ingredientFactory {public Veggie CreateVeggie(); public Cheese CreateCheese(); } / / concrete factory implementation public class CNHPizzaIngredientFactory: PizzaIngredientFactory { public Cheese CreateCheese() { return new CHNCheese(); } public Veggie CreateVeggie() { return new CHNVeggie(); } } public abstract class Veggie { } public class USVeggie : Veggie { } public class CHNVeggie : Veggie { } public abstract class Cheese { } public class USCheese : Cheese { } public class CHNCheese : Cheese { }Copy the code

  

Then re-cook Pizza

public abstract class Pizza { public String Name; Veggie veggie; Cheese cheese; // Prepare public abstract void Prepare(); Public void Bake() {} public void Cut() {} public void Box() {}Copy the code

Veggie and Cheese were added as abstract ingredients, while Prepare became an abstract method, letting its concrete subclasses decide what ingredients to use to make different pizza styles. Then we redo the subclass, using CheesePizza as an example

Public class ingredients: Pizza {PizzaIngredientFactory IngredientFactory; public CheesePizza(PizzaIngredientFactory IngredientFactory) { this.IngredientFactory = IngredientFactory; } public override void Prepare() { IngredientFactory.CreateCheese(); }}Copy the code

Modify the Chinese pizzeria

public class CHNSytlePizzaStore : PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza = null; / / create the Chinese raw materials factory CNHPizzaIngredientFactory ingredientFactory = new CNHPizzaIngredientFactory (); if (type == "cheese") { pizza = new CheesePizza(ingredientFactory); } else if (type == "viggie") { pizza = new VeggiePizza(ingredientFactory); } return pizza; }}Copy the code

  

Through this series of transformations we introduced a new type of factory, the so-called abstract factory, which creates raw materials.

With the abstract factory our code is decoupled from the actual factory so that if our factory needs to be extended we can modify the extension in a subclass.

Similarities and differences between the factory approach and the abstract factory

Same: both are used to create objects.

Different: The factory method uses inheritance, while the abstract factory uses composition.

Advantages: The factory approach is only responsible for decoupling from concrete types, and abstract factories are good for assembling a group of related products.

Disadvantages: The abstract factory extension interface needs to be modified for each subclass.