Differences between simple factories, factory methods, Abstract factories, strategy patterns, strategies and factories
Combined with simple examples and UML diagrams, the simple principle of factory pattern is explained.
I. Introduction
Ten years ago, there was an explosive family who had three cars (Benz, Bmw, Audi) and hired drivers to drive for him. Nevertheless, it's always the case when the outbreak households ride: get on the Benz and say to the driver, "Drive a Mercedes-Benz!" After getting on the Bmw, he said, "Drive a BMW!" After getting on Audi, he said, "Drive an Audi!"
You must say: This man is sick! Just say it's not enough to drive?! And when we put the behavior of this outbreaker into our programming language, we find that C language has always been riding in this way!
Fortunately, this diseased phenomenon can be avoided in OO language. The following is a Java language-based introduction to the theme of our article: factory mode!
II. Brief Introduction
The factory pattern mainly provides an interface for creating objects. Factory patterns are classified into three categories according to the formulation in Java and Patterns:
1. Simple Factory
2. Factory Method
3. Abstract Factory
These three patterns are abstract from top to bottom and more general. Another taxonomy is to consider the simple factory model as a special case of the factory method model, and classify the two into one category. Both can be done, this is to use the classification method of Java and Patterns.
In what circumstances should we remember to use the factory model? There are two main points:
1. You can't predict what kind of instance you need to create when coding.
2. The system should not depend on the details of how product class instances are created, assembled, and expressed.
What benefits can factory mode bring to our OOD and OOP?
3. Simple Factory Model
The model itself is simple and can be used in simpler business situations. Usually for small projects or specific products that rarely expand (so that factory classes don't have to change frequently).
It consists of three roles:
Factory role: This is the core of this model. It contains certain business logic and judgment logic. According to different logic, it produces specific factory products. For example, the Driver class in the example.
Abstract product roles: it is generally the parent class inherited by a specific product or the interface implemented. It is implemented by interfaces or abstract classes. The Car interface in the example.
Specific product roles: The object created by the factory class is an instance of this role. It is implemented by a specific class in java, such as Benz and Bmw classes in the example.
To use class diagrams to clearly express the relationship between them:
Here's how to treat that upstart: After using the simple factory model, now the upstart just sits in the car and says, "Drive" to the driver. Let's see how it can be implemented in code: (For convenience, all classes are in one file, so one class is declared public)
//Abstract products abstract class Car{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //Specific products class Benz extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } class Bmw extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } //Simple Factory class Driver{ public static Car createCar(String car){ Car c = null; if("Benz".equalsIgnoreCase(car)) c = new Benz(); else if("Bmw".equalsIgnoreCase(car)) c = new Bmw(); return c; } } //Boss public class BossSimplyFactory { public static void main(String[] args) throws IOException { //The boss told the driver that I was riding Mercedes-Benz today. Car car = Driver.createCar("benz"); car.setName("benz"); //The driver set off in Mercedes-Benz. car.drive(); } <span style="font-family: courier new,courier;">}</span>
If the boss wants to sit in Audi, the same is true.
This is the simple factory model. So what benefits does it bring?
Firstly, it fits the reality; moreover, the client is exempt from the responsibility of creating product objects directly, and is only responsible for "consuming" products (as the upstart does).
Next, we will analyze the simple factory model from the principle of opening and closing. When the upstart adds a car, as long as it meets the contract of abstract products, it can be used by customers as long as the factory class is informed. (i.e. creating a new car class and inheriting abstract product Car) So for the part of the product, it conforms to the open-close principle - opening to expansion and closing to modification; but the factory class is not ideal, because every additional car needs to add the corresponding business logic and judgment logic in the factory class, which is natural. It violates the principle of opening and closing.
In practical application, it is likely that the product is a multi-level tree structure. Since there is only one factory class in the simple factory model for these products, it may damage our God class.
As I mentioned earlier, the simple factory model applies to situations where business is simple or specific products are rarely added. It may not be well suited to complex business environments. This should come from the factory method mode!!
IV. Factory Method Model
Abstract factory role: this is the core of the factory method pattern, which is application-independent. It is the interface that a specific factory role must implement or the parent class that must inherit. In java, it is implemented by abstract classes or interfaces.
Specific factory roles: It contains code related to specific business logic. Objects invoked by the application to create the corresponding specific product. In java, it is implemented by specific classes.
Abstract product role: it is the parent class inherited by a specific product or the interface implemented. There are abstract classes or interfaces to implement in java.
Specific product roles: The object created by a specific factory role is an example of that role. It is implemented by specific classes in java.
To use class diagrams to clearly express the relationship between them:
In other words, the upstart business is getting bigger and bigger, and his car is becoming more and more popular. This is bitter that driver master, what car it must remember, maintenance, must pass through him to use! So the upstart sympathized with him and said, "I'll assign you a few hands. You just have to take care of them!" So the management of the factory method pattern appeared. The code is as follows:
//Abstract products abstract class Car{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //Specific products class Benz extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } class Bmw extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } //Abstract factory abstract class Driver{ public abstract Car createCar(String car) throws Exception; } //Specific factory (each factory is responsible for a specific product) class BenzDriver extends Driver{ public Car createCar(String car) throws Exception { return new Benz(); } } class BmwDriver extends Driver{ public Car createCar(String car) throws Exception { return new Bmw(); } } //Boss public class Boss{ public static void main(String[] args) throws Exception { Driver d = new BenzDriver(); Car c = d.createCar("benz"); c.setName("benz"); c.drive(); } }
Use the open-close principle to analyze the mode of factory method. When a new product (i.e. an upstart's car) comes into being, it can be used by customers without modifying any existing code as long as it is generated according to contracts provided by abstract product roles and abstract factory roles. (that is, when there is a new product, as long as the abstract product is created and based; new concrete factories inherit Abstract factories; without modifying any of the classes) the factory method pattern is completely in line with the open-close principle!
The factory approach model is sufficient to meet most of the business needs that we may encounter. But when there are many kinds of products, there will be a large number of corresponding factory classes, which should not be what we want. So I recommend a combination of simple factory mode and factory method mode in this case to reduce the factory class: that is, for similar species on the product tree (usually brothers in the leaves of the tree) using simple factory mode to achieve.
Of course, special cases need special treatment: there are different product trees in the system, and there are product families in the product tree (the term will be explained in the next section). In this case, the abstract factory pattern may be used.
V. Summary
Let's take a look at the Enlightenment of simple factory model and factory method model.
If we don't use factory mode to implement our example, maybe the code will be much less -- just to implement the existing car, not polymorphism. But in terms of maintainability, scalability is very poor (you can imagine adding a class that will be affected by a car). Therefore, in order to improve scalability and maintainability, it is worthwhile to write more code.
6. Abstract Factory Model
Let's first understand what a product family is: a family of functionally related products located in different product hierarchies.
The BmwCar and BenzCar in the figure are two product trees (product hierarchy); the BenzSportsCar and BmwSportsCar in the figure are one product family. They can all be placed in the sports car family, so the functions are relevant. Similarly, Bmw BussinessCar and Benz BusinessCar are also a product family.
It can be said that the difference between it and the factory method pattern lies in the complexity of creating objects. And the abstract factory model is the most abstract and general of the three. The purpose of the abstract factory pattern is to provide an interface for clients to create product objects in multiple product families.
And the abstract factory model also meets the following conditions:
1. There are many product families in the system, and the system can only consume one of them at a time.
2. Products belonging to the same product family are used by them.
Look at the roles of the abstract factory model (as in the factory approach):
Abstract factory role: this is the core of the factory method pattern, which is application-independent. It is the interface that a specific factory role must implement or the parent class that must inherit. In java, it is implemented by abstract classes or interfaces.
Specific factory roles: It contains code related to specific business logic. Objects invoked by the application to create the corresponding specific product. In java, it is implemented by specific classes.
Abstract product role: it is the parent class inherited by a specific product or the interface implemented. There are abstract classes or interfaces to implement in java.
Specific product roles: The object created by a specific factory role is an example of that role. It is implemented by specific classes in java.
//Abstract products (Bmw and Audi are the same) abstract class BenzCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //Specific products (Bmw and Audi are the same) class BenzSportCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzSportCar-----------------------"); } } class BenzBusinessCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzBusinessCar-----------------------"); } } abstract class BmwCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class BmwSportCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwSportCar-----------------------"); } } class BmwBusinessCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwBusinessCar-----------------------"); } } abstract class AudiCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class AudiSportCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiSportCar-----------------------"); } } class AudiBusinessCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiBusinessCar-----------------------"); } } //Abstract factory abstract class Driver3{ public abstract BenzCar createBenzCar(String car) throws Exception; public abstract BmwCar createBmwCar(String car) throws Exception; public abstract AudiCar createAudiCar(String car) throws Exception; } //Specific factories class SportDriver extends Driver3{ public BenzCar createBenzCar(String car) throws Exception { return new BenzSportCar(); } public BmwCar createBmwCar(String car) throws Exception { return new BmwSportCar(); } public AudiCar createAudiCar(String car) throws Exception { return new AudiSportCar(); } } class BusinessDriver extends Driver3{ public BenzCar createBenzCar(String car) throws Exception { return new BenzBusinessCar(); } public BmwCar createBmwCar(String car) throws Exception { return new BmwBusinessCar(); } public AudiCar createAudiCar(String car) throws Exception { return new AudiBusinessCar(); } } //Boss public class BossAbstractFactory { public static void main(String[] args) throws Exception { Driver3 d = new BusinessDriver(); AudiCar car = d.createAudiCar(""); car.drive(); } }
Among them: Benz SportCar and Benz BusinessCar belong to the product tree; similarly, Bmw SportCar and Bmw BusinessCar. Benz SportCar and Bmw SportCar and AdiSportCar belong to the product family.
Therefore, abstract factory pattern is generally used in scenarios with product tree and product family.
Disadvantage of abstract factory model: If you need to add a new product tree, you need to add three new product classes, such as VolvoCar, VolvoSportCar,VolvoSportCar, and you need to modify three factory classes. It's ugly to make such a lot of changes.
So we can use simple factories with reflection to improve Abstract factories:
UML diagram.
abstract class BenzCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class BenzSportCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzSportCar-----------------------"); } } class BenzBusinessCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzBusinessCar-----------------------"); } } abstract class BmwCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class BmwSportCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwSportCar-----------------------"); } } class BmwBusinessCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwBusinessCar-----------------------"); } } abstract class AudiCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class AudiSportCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiSportCar-----------------------"); } } class AudiBusinessCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiBusinessCar-----------------------"); } } /** * Simple factories improve Abstract factories and their subfactories by reflection * @author Administrator * */ class Driver3{ public static BenzCar createBenzCar(String car) throws Exception { return (BenzCar) Class.forName(car).newInstance(); } public static BmwCar createBmwCar(String car) throws Exception { return (BmwCar) Class.forName(car).newInstance(); } public static AudiCar createAudiCar(String car) throws Exception { return (AudiCar) Class.forName(car).newInstance(); } } //Client public class SimpleAndAbstractFactory { public static void main(String[] args) throws Exception { AudiCar car = Driver3.createAudiCar("com.java.pattendesign.factory.AudiSportCar"); car.drive(); } }
Strategic model
According to the term strategy, the strategy mode is a behavioral mode. It is a bit similar to the battle plan when looking for a battle. Commanders usually make several different plans according to the actual situation before fighting. If the situation changes at that time, they will decide which one to replace the original plan according to the corresponding conditions. However, no matter how many replacements are made, the battle will still be fought.
For example, the functions of exporting to EXCEL, WORD and PDF files are different, but most of them are the same.
The strategy pattern and the factory pattern are basically the same from the uml diagram. It just emphasizes different encapsulations. We explain the strategy model by comparing the factory model with the strategy model.
The factory model can be understood as follows: Assuming that Audi produces cars, it has a core technology to produce cars. On the other hand, it produces different models of cars and assembles them on different production lines. When customers make reservations through the sales department, Audi will produce the cars they need on the designated production line.
Strategy pattern is similar to factory pattern in structure. The only difference is that factory pattern instantiates the operation of a product on the server side. In other words, the client communicates only some kind of identity to the server, and the server instantiates an object according to that identity. The client of the policy pattern conveys an instance to the server, which only takes the instance to execute in the environment of the server. It's like a person who doesn't know much about cars buying a car. He compares what he wants and what he wants. The Sales Department forms an order according to his comparisons. This is the way he works under the factory model. Under the strategy model, the customer is an expert. He gives the detailed information of the order himself. The sales department just turns around and gives it to the production department. By comparison, it is not difficult to find that the factory model must provide a flexible sales department. If users have new needs, the sales department must immediately realize that in order to make the right order. So if a new car comes out, the production and sales departments need to update, and for customers, they also need to update the description of the new car, so there are three places that need to be changed. However, the sales department in the strategy mode has a relatively fixed job. It is only responsible for accepting orders and performing specific operations. When a new car comes out, it only needs to update the code of the production department and the client side of the service side, instead of the code of the sales department.
Technical support: Simple factories and strategies are based on object-oriented encapsulation and polymorphism. The idea they implement is to set up an abstract model and derive from it various methods that meet different customer needs and encapsulate them.
The difference between factory mode and policy mode is that the location of instantiating an object is different. For factory mode, the instantiated object is placed on the server side, that is, in the factory class.
The strategy pattern instantiates the operation of the object on the client side, and the "sales department" on the server side is only responsible for passing the object and performing specific operations in the environment on the server side.
The factory model requires that the sales department at the service end be sensitive enough, and the strategy model is encapsulated, so the sales department is silly and needs customers to provide enough parameters to distinguish which strategy to use, and the best example is the strategy.
//Abstract products abstract class AudiCar{ private String name; public abstract void makeCar(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //Specific products class AudiA6 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } class AudiA4 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } //Sales Department - Server class CarContext { AudiCar audiCar = null; public CarContext(AudiCar audiCar) { this.audiCar = audiCar; } public void orderCar(){ this.audiCar.makeCar(); } } //Client - Client (this customer is expert, knows everything, he said I want A6, the sales department immediately give him a6, so the sales department does not need to understand very well) public class SimplyFactoryAndStrategy2 { public static void main(String[] args) throws IOException { //When the customer says what kind of car I want, the salesman knows what kind of car he wants. AudiCar car = new AudiA6(); car.setName("a6"); CarContext context = new CarContext(car); context.orderCar(); } } //Factory Model - Comparing with the above Strategic Model //Abstract products abstract class AudiCar{ private String name; public abstract void makeCar(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //Specific products class AudiA6 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } class AudiA4 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } //Simple Factory - Sales Department - Server class CarFactroy{ public static AudiCar createCar(String car){ AudiCar c = null; if("A6".equalsIgnoreCase(car)) c = new AudiA6(); else if("A4".equalsIgnoreCase(car)) c = new AudiA4(); return c; } } //Client - Client (this customer is a layman, who knows nothing, as long as he gets off the car casually, the sales department can know which car he wants, so the sales department is better.) public class SimplyFactoryAndStrategy { public static void main(String[] args) throws IOException { System.out.print("Please enter the car you want to take:( A6,A4)"); String carName = new BufferedReader(new InputStreamReader(System.in)).readLine(); //When the customer says what kind of car I want, the salesman knows what kind of car he wants. AudiCar car = CarFactroy.createCar(carName); car.setName(carName); car.makeCar(); } }
Advantages and disadvantages of strategy model
The main advantages of the strategy model are:
- Policy classes can be switched freely, because they are implemented from the same abstraction, so they can be switched freely.
- Easy to expand, adding a new strategy is very easy for policy mode, basically can be extended without changing the original code.
- Avoid using multiple conditions. If you don't use policy patterns, you must use conditional statements to connect all algorithms, and use conditional judgment to decide which algorithm to use. As we mentioned in the previous article, using multiple conditions judgment is very difficult to maintain.
There are two main shortcomings of the strategy model:
- Maintaining various policy classes can lead to additional overhead for development. Maybe everyone has experience in this area: Generally speaking, the number of policy classes is more than five, which is more headache.
- All policy classes must be exposed to the client (caller), because the client decides which strategy to use. Therefore, the client should know what strategy to use and the difference between different strategies. Otherwise, the consequences will be serious. For example, there is a strategy mode of sorting algorithm, which provides three algorithms: quick sorting, bubble sorting and selective sorting. Should the client understand the application of these three algorithms before using them? For example, if the client wants to use a container, either linked list or array, does the client also need to understand the difference between linked list and array? This is contrary to Dimiter's law.
Applicable scenarios
In object-oriented design, we must be familiar with strategy pattern, because it is essentially inheritance and polymorphism in object-oriented. After reading the general code of strategy pattern, I think that even if we have never heard of strategy pattern before, we must use it in the development process. At least in the following two cases, you can consider using the strategy model.
- The main logic of several classes is the same, only slightly different in arithmetic and behavior of partial logic.
- There are several similar behaviors, or algorithms, in which the client needs to dynamically decide which one to use, and then policy patterns can be used to encapsulate these algorithms for client invocation.
Strategic mode is a simple and commonly used mode. When we develop it, we often use it consciously or unconsciously. Generally speaking, strategic mode will not be used alone. It is often used in combination with template method mode and factory mode.
Large-grained if --else if... can be done using the factory + policy pattern.