catalogue
5.1 significance of factory mode
5.3 dependency abstraction principle of design pattern
1. Case requirements
Let's take a specific demand as a case to learn
If there is a pizza project, to facilitate the expansion of pizza types and maintenance, there are the following requirements
-
There are many kinds of pizza, such as cheese pizza, bacon pizza, fruit pizza and so on
-
The production of pizza includes the following stages: preparing raw materials, baking, cutting, packing, etc
-
Pizza ordering function
Now let's start coding for the above requirements
First, we write a Pizza abstract class
package org.colin.factory._01_first; //Abstract Pizza class public abstract class Pizza { //Pizza name protected String name; //Prepare raw materials. Different pizzas have different raw materials. Therefore, we make abstract methods and implement classes to select different raw materials public abstract void prepare(); //bake public void bake(){ System.out.println("Baking complete"); } //cutting public void cut(){ System.out.println("Cutting complete"); } //pack public void pack(){ System.out.println("Packaging complete"); } //Set pizza name public void setName(String name) { this.name = name; } }
Then write different kinds of pizza classes to inherit this abstract class and implement the abstract method. First write the cheese pizza class:
package org.colin.factory._01_first; //Cheese pizza public class CheesePizza extends Pizza{ @Override public void prepare() { System.out.println("Prepare raw materials for making [cheese] pizza"); } }
Bacon pizza:
package org.colin.factory._01_first; //Bacon Pizza public class BaconPizza extends Pizza{ @Override public void prepare() { System.out.println("Prepare raw materials for making [bacon] pizza"); } }
Fruit pizza:
package org.colin.factory._01_first; //Fruit pizza public class FruitPizza extends Pizza{ @Override public void prepare() { System.out.println("Prepare raw materials for making [fruit] pizza"); } }
The next step is to complete the pizza order class, where you can create different pizza objects according to the entered pizza types:
package org.colin.factory._01_first; //Order class interface public class PizzaController { public static void main(String[] args) { OrderPizza("Cheese");//Next order for cheese pizza } //Pizza type: enter pizza type and return different pizza objects public static void OrderPizza(String pizzaType) { Pizza pizza = null; if (pizzaType.equals("Cheese")){ //Cheese pizza pizza = new CheesePizza(); }else if (pizzaType.equals("Bacon")){ //Bacon Pizza pizza = new BaconPizza(); }else if (pizzaType.equals("Fruit")){ //Fruit pizza pizza = new FruitPizza(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.pack(); } }
Operation results:
Prepare raw materials for making [cheese] pizza Baking complete Cutting complete Packaging complete
If the OrderPizza parameter in main is passed into Fruit, it will be the order of the next Fruit pizza. The print results are as follows:
Prepare raw materials for making [fruit] pizza Baking complete Cutting complete Packaging complete
So far, we have completed all the requirements. It's done!
But is that all right?
You can look at the above code. The code of our new pizza is written in the pizza order class. Therefore, once we need to expand a new pizza category, in addition to creating a new pizza category class, we also need to modify the pizza order class and add code, which violates the opening and closing principle
At this point, we can consider using the factory mode
2. Simple factory mode
2.1 basic introduction
-
The simple factory mode belongs to the creation mode, which is a kind of factory mode.
-
The simple factory pattern is a factory object that determines which product class instance to create.
-
Simple factory mode is the simplest and most practical mode in the factory mode family.
-
Simple factory pattern: defines a class for creating objects, which encapsulates the behavior of instantiating objects.
-
In software development, when we will use a lot of to create a, a class or a batch of objects, we will use the factory pattern.
2.2 coding
How can we use the simple factory method to optimize the above case? First, we need to extract the pizza production code from the pizza order class PizzaController to form a simple factory class:
package org.colin.factory._02_simpleFactory; import org.colin.factory.first.BaconPizza; import org.colin.factory.first.CheesePizza; import org.colin.factory.first.FruitPizza; import org.colin.factory.first.Pizza; //Simple factory mode public class SimpleFactory { /** * Returns the corresponding Pizza object according to the Pizza type * @param pizzaType * @return Pizza */ public Pizza createPizza(String pizzaType){ Pizza pizza = null; System.out.println("Use [simple factory mode] to produce pizza......."); if (pizzaType.equals("Cheese")){ //Cheese pizza pizza = new CheesePizza(); }else if (pizzaType.equals("Bacon")){ //Bacon Pizza pizza = new BaconPizza(); }else if (pizzaType.equals("Fruit")){ //Fruit pizza pizza = new FruitPizza(); } return pizza; } }
The order class PizzaController can be changed to the following:
package org.colin.factory._02_simpleFactory; import org.colin.factory.first.Pizza; //Order class interface public class PizzaController { //Define a simple factory object private SimpleFactory simpleFactory; public PizzaController(SimpleFactory simpleFactory){ this.simpleFactory = simpleFactory; } //Pizza type: enter pizza type and return different pizza objects public void OrderPizza(String pizzaType) { Pizza pizza = this.simpleFactory.createPizza(pizzaType); if(pizza != null){ pizza.prepare(); pizza.bake(); pizza.cut(); pizza.pack(); }else{ System.out.println("Failed to order pizza"); } } } class TestPizza{ public static void main(String[] args) { PizzaController pizzaController = new PizzaController(new SimpleFactory()); pizzaController.OrderPizza("Cheese"); } }
Operation results:
Using [simple factory mode] to produce pizza Prepare raw materials for making [cheese] pizza Baking complete Cutting complete Packaging complete
The advantage of this modification is that if we need to expand the new pizza category, we only need to change the code in the simple factory class (i.e. SimpleFactory class), and there is no need to change elsewhere.
Next, for this case, there is a new demand as follows: when ordering Pizza, customers can order Pizza with different flavors, such as cheese Pizza in Beijing, pepper Pizza in Beijing, cheese Pizza in London and pepper Pizza in London.
There are two ideas at this time:
-
Use the simple factory mode to create different simple factory classes, such as BJPizzaSimpleFactory and ldpizza simplefactory. However, considering the scale of the project and the maintainability and scalability of the software, this idea is not very good
-
Use factory method mode
3. Factory method model
3.1 basic introduction
Factory method pattern: defines an abstract method to create an object, and the subclass determines the class to be instantiated
For the new requirements of the above case, we use the factory method pattern change. OrderPizza is an abstract factory class, in which the createPizza() method is an abstract method, Then the pizza factory classes of different regional flavors are defined: BJOrderPizza and ldorderpizza. These two pizza factory classes produce pizza in various regions respectively. The class diagram is as follows:
3.2 coding
The Pizza abstract class remains unchanged:
package org.colin.factory._03_factoryMethod; //Abstract Pizza class public abstract class Pizza { //Pizza name protected String name; //Prepare raw materials. Different pizzas have different raw materials. Therefore, we make abstract methods and implement classes to select different raw materials public abstract void prepare(); //bake public void bake(){ System.out.println("Baking complete"); } //cutting public void cut(){ System.out.println("Cutting complete"); } //pack public void pack(){ System.out.println("Packaging complete"); } //Set pizza name public void setName(String name) { this.name = name; } }
Bjchiesepizza class:
package org.colin.factory._03_factoryMethod; //Beijing cheese pizza public class BJCheesePizza extends Pizza{ @Override public void prepare() { System.out.println("Beijing flavor [cheese] pizza is preparing raw materials....."); } }
BJBaconPizza class:
package org.colin.factory._03_factoryMethod; //Beijing style bacon pizza public class BJBaconPizza extends Pizza{ @Override public void prepare() { System.out.println("Beijing style [bacon] pizza is preparing raw materials....."); } }
BJFruitPizza class:
package org.colin.factory._03_factoryMethod; //Beijing flavor fruit pizza public class BJFruitPizza extends Pizza{ @Override public void prepare() { System.out.println("Beijing flavor [fruit] pizza is preparing raw materials....."); } }
Here are three categories of London flavors:
package org.colin.factory._03_factoryMethod; //London cheese pizza public class LDCheesePizza extends Pizza{ @Override public void prepare() { System.out.println("London flavor [cheese] pizza, raw materials are being prepared....."); } }
package org.colin.factory._03_factoryMethod; //London Style bacon pizza public class LDBaconPizza extends Pizza{ @Override public void prepare() { System.out.println("London flavor [bacon] pizza, raw materials are being prepared....."); } }
package org.colin.factory._03_factoryMethod; //London fruit pizza public class LDFruitPizza extends Pizza{ @Override public void prepare() { System.out.println("London flavor [fruit] pizza, raw materials are being prepared....."); } }
Abstract factory class OrderPizza:
package org.colin.factory._03_factoryMethod; //Abstract factory class public abstract class OrderPizza { /** * Define an abstract method * @param pizzaType * @return Pizza */ public abstract Pizza createPizze(String pizzaType); public OrderPizza(String orderType) { //The abstract method is completed by the factory subclass Pizza pizza = createPizze(orderType); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.pack(); } }
The two factory subclasses implement the OrderPizza class respectively:
package org.colin.factory._03_factoryMethod; //Beijing taste order public class BJOrderPizza extends OrderPizza { public BJOrderPizza(String orderType) { super(orderType); } @Override public Pizza createPizze(String pizzaType) { Pizza pizza = null; if (pizzaType.equals("Cheese")){ //Cheese pizza pizza = new BJCheesePizza(); }else if (pizzaType.equals("Bacon")){ //Bacon Pizza pizza = new BJBaconPizza(); }else if (pizzaType.equals("Fruit")){ //Fruit pizza pizza = new BJFruitPizza(); } return pizza; } }
package org.colin.factory._03_factoryMethod; //London taste order public class LDOrderPizza extends OrderPizza { public LDOrderPizza(String orderType) { super(orderType); } @Override public Pizza createPizze(String pizzaType) { Pizza pizza = null; if (pizzaType.equals("Cheese")){ //Cheese pizza pizza = new LDCheesePizza(); }else if (pizzaType.equals("Bacon")){ //Bacon Pizza pizza = new LDBaconPizza(); }else if (pizzaType.equals("Fruit")){ //Fruit pizza pizza = new LDFruitPizza(); } return pizza; } }
Test class:
package org.colin.factory._03_factoryMethod; public class PizzaTest { public static void main(String[] args) { //Create all kinds of pizza with Beijing flavor new LDOrderPizza("Cheese"); } }
4. Abstract factory pattern
4.1 basic introduction
-
The abstract factory pattern defines an interface for creating clusters of related or dependent objects without specifying specific classes
-
Abstract factory pattern can integrate simple factory pattern and factory method pattern
-
From the design level, the abstract factory pattern is an improvement (or further abstraction) of the simple factory pattern
-
The factory is abstracted into two layers, the abstract factory and the factory subclass of concrete implementation. Programmers can use the corresponding factory subclasses according to the type of object they create. This turns a single simple factory class into a factory cluster, which is more conducive to code maintenance and expansion
After the above pizza case uses the abstract factory mode, the class diagram is as follows:
4.2 coding
Next, let's implement it. The abstract factory interface is as follows:
package org.colin.factory._04_abstractFactory; //Abstract layer of abstract factory pattern public interface AbsFactory { /** * Implemented by the following concrete subclasses * @param pizzaType * @return */ Pizza createPizza(String pizzaType); }
Two specific plant subclasses:
package org.colin.factory._04_abstractFactory; //Specific factory subclass public class BJFactory implements AbsFactory{ @Override public Pizza createPizza(String pizzaType) { System.out.println("The abstract factory pattern is used"); Pizza pizza = null; if (pizzaType.equals("Cheese")){ //Cheese pizza pizza = new BJCheesePizza(); }else if (pizzaType.equals("Bacon")){ //Bacon Pizza pizza = new BJBaconPizza(); }else if (pizzaType.equals("Fruit")){ //Fruit pizza pizza = new BJFruitPizza(); } return pizza; } }
package org.colin.factory._04_abstractFactory; //Specific factory subclass public class LDFactory implements AbsFactory{ @Override public Pizza createPizza(String pizzaType) { System.out.println("The abstract factory pattern is used"); Pizza pizza = null; if (pizzaType.equals("Cheese")){ //Cheese pizza pizza = new LDCheesePizza(); }else if (pizzaType.equals("Bacon")){ //Bacon Pizza pizza = new LDBaconPizza(); }else if (pizzaType.equals("Fruit")){ //Fruit pizza pizza = new LDFruitPizza(); } return pizza; } }
Pizza order category:
package org.colin.factory._04_abstractFactory; //Pizza order class public class OrderPizza { public AbsFactory absFactory; public OrderPizza(AbsFactory absFactory){ this.absFactory = absFactory; } public void setOrder(String pizzaType){ Pizza pizza = this.absFactory.createPizza(pizzaType); if (pizza != null){ pizza.prepare(); pizza.bake(); pizza.cut(); pizza.pack(); }else{ System.out.println("Failed to order pizza"); } } }
Test class:
package org.colin.factory._04_abstractFactory; public class Test { public static void main(String[] args) { //Here is a demonstration of the factory subclass in Beijing OrderPizza orderPizza = new OrderPizza(new BJFactory()); orderPizza.setOrder("Cheese"); } }
Operation results:
The abstract factory pattern is used Beijing flavor [cheese] pizza is preparing raw materials Baking complete Cutting complete Packaging complete
5. Summary
5.1 significance of factory mode
Abstract the code of the instantiated object and put it into a class for unified management and maintenance, so as to decouple the dependency relationship with the main project, so as to improve the expansion and maintenance of the project.
5.2. Three factory modes
-
Simple factory mode
-
Factory method model
-
Abstract factory pattern
5.3 dependency abstraction principle of design pattern
-
When creating an object instance, do not directly the new class, but put the action of the new class in a factory method and return. It can also be said that variables do not directly hold references to specific classes.
-
Instead of letting classes inherit concrete classes, they inherit abstract classes or implement interface s.
-
Do not override methods already implemented in the base class.