Original articles, reproduced please be sure to place the following paragraph at the beginning of the article.
This article is forwarded from Jason's Blog, the original link http://www.jasongj.com/design_pattern/simple_factory
Simple Factory mode use case
There is an abstract product-automobile (car), and there are a variety of specific sub-products, such as Benzcar,bmwcar,landrovercar. The class diagram is as follows
As a driver, if you want to open one of the vehicles, such as Benzcar, the most straightforward way is to directly create an instance of Benzcar, and execute its drive method, as follows
package com.jasongj.client;import com.jasongj.product.BenzCar;publicclass Driver1 { publicstaticvoidmain(String[] args) { new BenzCar(); car.drive(); }}
If you want to open land Rover instead, you need to modify the code, create an instance of the land Rover, and execute its drive method. This means that any time you need to change a car, you have to modify the client code.
A slightly better way to do this is by reading the configuration file, getting the car that needs to be opened, then creating the appropriate instance and pointing to it by a reference to the parent class car, using polymorphic execution of different car drive methods. As follows
PackageCom.jasongj.client;ImportOrg.apache.commons.configuration.ConfigurationException;ImportOrg.apache.commons.configuration.XMLConfiguration;ImportOrg.slf4j.Logger;ImportOrg.slf4j.LoggerFactory;ImportCom.jasongj.product.BMWCar;ImportCom.jasongj.product.BenzCar;ImportCom.jasongj.product.Car;ImportCom.jasongj.product.LandRoverCar; Public class Driver2 { Private Static FinalLogger LOG = Loggerfactory.getlogger (Driver2.class); Public Static void Main(string[] args)throwsconfigurationexception {xmlconfiguration config =NewXmlconfiguration ("Car.xml"); String name = Config.getstring ("Driver2.name"); Car car;Switch(name) { Case "Land Rover": Car =NewLandrovercar (); Break; Case "BMW": Car =NewBmwcar (); Break; Case "Benz": Car =NewBenzcar (); Break;default: Car =NULL; Break; } log.info ("Created car name is {}", name); Car.drive (); }}
For car users, only parameters can be used to specify the type of car you need and get its instance, and no matter which car you use, you do not need to modify the subsequent operation of car. At this point, prototypes of the simple factory model have been formed. The simple factory pattern is implemented if the above logical judgments are encapsulated in a static method of a specialized class. Factory code is as follows
PackageCom.jasongj.factory;ImportOrg.apache.commons.configuration.ConfigurationException;ImportOrg.apache.commons.configuration.XMLConfiguration;ImportOrg.slf4j.Logger;ImportOrg.slf4j.LoggerFactory;ImportCom.jasongj.product.BMWCar;ImportCom.jasongj.product.BenzCar;ImportCom.jasongj.product.Car;ImportCom.jasongj.product.LandRoverCar; Public class CarFactory1 { Private Static FinalLogger LOG = Loggerfactory.getlogger (Carfactory1.class); Public StaticCarNewcar() {Car car =NULL; String name =NULL;Try{xmlconfiguration config =NewXmlconfiguration ("Car.xml"); Name = Config.getstring ("Factory1.name"); }Catch(ConfigurationException ex) {Log.error ("Parse XML configuration file failed", ex); }Switch(name) { Case "Land Rover": Car =NewLandrovercar (); Break; Case "BMW": Car =NewBmwcar (); Break; Case "Benz": Car =NewBenzcar (); Break;default: Car =NULL; Break; } log.info ("Created car name is {}", name);returnCar }}
The caller code is as follows
package com.jasongj.client;import com.jasongj.factory.CarFactory1;import com.jasongj.product.Car;publicclass Driver3 { publicstaticvoidmain(String[] args) { Car car = CarFactory1.newCar(); car.drive(); }}
Compared with Driver2, all the judgment logic is encapsulated in the factory (CarFactory1), Driver3 no longer need to care about the instantiation of car, to achieve the object creation and use of isolation.
Of course, the Simple Factory mode does not require that a configuration file be read to determine which class to instantiate, and parameters can be passed in as parameters to the factory static method.
Simple Factory mode advanced use reflection for extensibility
As you can see from the implementation of Driver2 and CarFactory1, when new cars are added, the code that needs to be updated Driver2 and CarFactory1 will also support the new car. This is a violation 开闭原则
(Open-close Principle). You can use Reflection (Reflection) to resolve the problem.
PackageCom.jasongj.factory;ImportOrg.apache.commons.configuration.ConfigurationException;ImportOrg.apache.commons.configuration.XMLConfiguration;ImportOrg.slf4j.Logger;ImportOrg.slf4j.LoggerFactory;ImportCom.jasongj.product.Car; Public class CarFactory2 { Private Static FinalLogger LOG = Loggerfactory.getlogger (Carfactory2.class); Public StaticCarNewcar() {Car car =NULL; String name =NULL;Try{xmlconfiguration config =NewXmlconfiguration ("Car.xml"); Name = Config.getstring ("Factory2.class"); }Catch(ConfigurationException ex) {Log.error ("Parsing XML configuration file failed", ex); }Try{car = (car) class.forname (name). newinstance (); Log.info ("Created Car class name is {}", name); }Catch(Instantiationexception | illegalaccessexception | ClassNotFoundException e) {log.error ("Instantiate car {} failed", name); }returnCar }}
As you can see from the code above, if you need to introduce a new car, just specify the full class name of the car in the configuration file (including the package name), and CarFactory2 can instantiate it by reflection. The opening of the extension is achieved, while the closure of the modification is ensured. Readers familiar with spring should think of the spring IOC implementation.
Annotations make simple Factory mode not simple
In the example above, the reflection was used to open the extension and close the modification. Sometimes, however, it is not convenient to use the full name of the class, and it is more appropriate to use aliases. For example, each bean in spring has an ID, which is referenced by ID when referencing the bean. Data flow tools like Apache Nifi Use the responsibility chain pattern on the process, whereas for a single processor, the factory is used, and the user-defined processor does not need to be registered through the code, but instead uses annotations (to make it easier to understand the code below, Please read the author's other article "Java Series (i) Annotation (annotations)").
The following section continues to use annotations to upgrade the simple factory model based on the above case.
PackageCom.jasongj.factory;ImportJava.util.Collections;ImportJava.util.Map;ImportJava.util.Set;ImportJava.util.concurrent.ConcurrentHashMap;ImportOrg.apache.commons.configuration.ConfigurationException;ImportOrg.apache.commons.configuration.XMLConfiguration;ImportOrg.reflections.Reflections;ImportOrg.slf4j.Logger;ImportOrg.slf4j.LoggerFactory;ImportCom.jasongj.annotation.Vehicle;ImportCom.jasongj.product.Car; Public class CarFactory3 { Private Static FinalLogger LOG = Loggerfactory.getlogger (Carfactory3.class);Private StaticMap<string, class> Allcars;Static{Reflections Reflections =NewReflections ("Com.jasongj.product"); set<class<?>> annotatedclasses = Reflections.gettypesannotatedwith (Vehicle.class); Allcars =NewConcurrenthashmap<string, class> (); for(class<?> classobject:annotatedclasses) {Vehicle Vehicle = (Vehicle) classobject.getannotation (Vehicle.class); Allcars.put (Vehicle.type (), classobject); } Allcars = Collections.unmodifiablemap (allcars); } Public StaticCarNewcar() {Car car =NULL; String type =NULL;Try{xmlconfiguration config =NewXmlconfiguration ("Car.xml"); Type = config.getstring ("Factory3.type"); Log.info ("car type is {}", type); }Catch(ConfigurationException ex) {Log.error ("Parsing XML configuration file failed", ex); }if(Allcars.containskey (type)) {Log.info ("created car type is {}", type);Try{car = (car) allcars.get (type). newinstance (); }Catch(Instantiationexception | Illegalaccessexception ex) {Log.error ("Instantiate car Failed", ex); } }Else{Log.error ("specified car type {} does not exist", type); }returnCar }}
As you can see from the code above, the factory scans all the car vehicle annotations (each car declares its type in the note and can be used as an alias for that car) and then establishes a class original mapping of the car alias to the specific car. At this point the static method of the factory can instantiate the corresponding car according to the target alias.
All the code in this article is available for download from author GitHub.
Simple Factory mode details Simple Factory mode definition
Simple Factory pattern is also called the Static Factory method mode (FactoryMethod pattern). Specifically defining a class (such as CarFactory1, CarFactory2, CarFactory3, above) is responsible for creating an instance of another class, which determines which specific class to instantiate, thus avoiding explicit designation in client code and decoupling of the implementation. This class is called a factory class because it can create different subclass objects under the same abstract class (or interface), just like a factory.
Simple Factory mode class diagram
The simple factory pattern class diagram is shown below
Simple Factory Model Role Division
- Factory role (as in CARFACTORY1/2/3 above): This is the core of the simple factory pattern, which is responsible for creating the internal logic of all classes. Of course the factory class must be able to be called outside to create the desired product object. In general, the factory class provides a static method by which an external program creates the desired object.
- Abstract product role (as in car above): The parent class of all objects created by the simple Factory mode. Note that the parent class here can be an interface or an abstract class, which is responsible for describing the common interfaces common to the created instances.
- Specific product roles (as in Bmwcar,benzcar,landrovercar above): The specific instance objects created by a simple factory, which often have a common parent class.
Simple Factory Model Benefits
- The factory class is the key to the entire simple factory model. It contains the necessary judgment logic to determine which specific class of objects should be created, based on information given by the outside world (configuration, or parameters). Users can create the required instances directly from the factory class, without having to know how they are created and how they are organized. facilitates the optimization of the entire software architecture.
- By introducing configuration files and reflection, you can change and add new product classes without modifying any client code, to some extent improving the flexibility of the system (such as CarFactory2).
- The client does not need to know the class name of the specific product class created, only need to know the corresponding parameters of the specific product class, for some complex class names, through the simple Factory mode can reduce the user's memory (such as CarFactory3).
Simple Factory mode and principles that OOP principles have followed
- Dependency Inversion principle
- Dimitri Law
- The principle of the Richter replacement
- Interface Isolation principle
Principles that are not followed
- Open/Close principle (as described above, this can be avoided with configuration files + reflection or annotations)
- Single principle of responsibility (factory class is responsible for logical judgment and also responsible for instance creation)
Simple Factory mode Disadvantage
- Because the factory class centralizes the creation logic of all instances, this directly leads to the involvement of all clients once the factory has a problem.
- Since the product of the simple factory model is based on a common abstract class or interface, so that when the type of product is increased, that is, when there are different product interfaces or abstract classes, the factory class needs to determine when to create the product of which interface, which is confused with what kind of product is created. Violates the principle of single responsibility, resulting in loss of flexibility and maintainability of the system.
- As mentioned above, in general (such as CarFactory1), the simple factory model violates the "open-close principle" because when we add a new product we have to modify the factory class, the corresponding factory class needs to be recompiled again. But this can be solved to some extent (such as CarFactory2) using reflection (CarFactory3 is also using reflection in nature).
- Simple Factory mode due to the use of the static factory method, the factory role cannot form an inheritance-based hierarchy. This is a reservation, because inheritance is not an end, and if there is no such requirement, it is not a disadvantage, such as the DriverManager of JDBC.
Typical application of the simple factory model in the JDK
Simple Factory mode The most typical application in the JDK is JDBC. The relational database can be regarded as an abstract product, and the specific relational database (Mysql,postgresql,oracle) provided by each vendor is a specific product. DriverManager is a factory class. When an application uses a relational database through the JDBC interface, it does not need to be concerned with what kind of database is used, but directly using DriverManager's static method to get the connection of the database.
PackageCom.jasongj.client;ImportJava.sql.Connection;ImportJava.sql.DriverManager;ImportJava.sql.PreparedStatement;ImportJava.sql.SQLException;ImportOrg.slf4j.Logger;ImportOrg.slf4j.LoggerFactory; Public class JDBC { Private Static FinalLogger LOG = Loggerfactory.getlogger (Jdbc.class); Public Static void Main(string[] args) {Connection conn =NULL;Try{Class.forName ("Org.apache.hive.jdbc.HiveDriver"); conn = Drivermanager.getconnection ("Jdbc:hive2://127.0.0.1:10000/default"); PreparedStatement PS = conn.preparestatement ("SELECT COUNT (*) from Test.test"); Ps.execute (); }Catch(SQLException ex) {Log.warn ("Execute query Failed", ex); }Catch(ClassNotFoundException e) {Log.warn ("Load Hive driver failed", e); }finally{if(Conn! =NULL){Try{Conn.close (); }Catch(SQLException e) {//No-opt} } } }}
Java Design Pattern Series
- Java design pattern (i) Simple Factory mode is not simple
- Java design pattern (ii) Factory method mode
- Java design Pattern (iii) abstract Factory mode
- Java design Pattern (IV) Observer pattern
Java design mode (a) Simple Factory mode is not simple