What is a design pattern?
- Semantic concept: classic code to solve specific problems in object-oriented design.
- Narrow concept: 23 design modes defined by GOF4 Gang: factory, adapter, decorator, facade, agent, template
1, Factory design mode
What is factory design pattern?
Concept: create objects through factory classes;
Benefits: decoupling (coupling: Specifies the strong correlation between codes, and the change of one party will affect the other party.)
2, Create objects without using factory mode
Taking the creation of UserDao and UserService as an example, UserDao simulates the login operation of the database, and UserService simulates the related login business.
UserDao interface
public interface UserDao { void login(String name, String password); }
UserDaoImpl implements the UserDao interface
public class UserDaoImpl implements UserDao{ @Override public void login(String name, String password) { System.out.println("User login, name=" + name + ", password=" + password); } }
UserService interface
public interface UserService { void login(String name, String password); }
UserServiceImpl implements the UserService interface
public class UserServiceImpl implements UserService{ UserDao userDao = new UserDaoImpl(); @Override public void login(String name, String password) { System.out.println("Handle login business!"); userDao.login(name, password); } }
Test the Service created above.
@Test public void testService() { // Suppose there's a lot of code // create object UserService userService = new UserServiceImpl(); // Call method userService.login("Iron Man", "xxxxx"); // Suppose there's a lot of code }
The operation result is:
Handle login business! User login, name=Iron Man, password=xxxxx
In the test file, you need to select the instantiation method of UserService. There is code coupling here. If you use other instantiation methods, you need to modify here. For example, if you want to use the instantiation method UserServiceImpl2() instead, the statement to create the object should be changed to:
UserService userService = new UserServiceImpl2();
At this time, it's really simple for us to modify it. We just need to change the instantiation method. However, if the code is very large, you need to find this paragraph in a large amount of code for modification, which will become very troublesome.
How to change the instantiation method without modifying this code? The factory mode needs to be introduced here.
3, Simple factory design
3.1 simple factory - create objects directly
At this point, you need to create a factory class and a static method in the project class to return an implementation class.
Factory design
public class BeanFactory { public static UserService getUserService() { return new UserServiceImpl(); } }
Create the test code and get the UserService object through getUserService() in the factory class.
@Test public void testService2() { // Suppose there's a lot of code // create object UserService userService = BeanFactory.getUserService(); // Call method userService.login("Iron Man", "xxxxx"); // Suppose there's a lot of code }
The operation results are as follows:
Handle login business! User login, name=Iron Man, password=xxxxx
This solves the coupling problem above. If you use other implementation methods, you only need to modify the returned object in the factory class.
However, this transfers the coupling to the factory class and the objects that need to be modified in the factory class.
3.2 improvement Factory 1 - create objects by reflection
Next, the factory class is improved to create objects through reflection to achieve the effect of decoupling.
public class BeanFactory { public static UserService getUserService() { UserService userService = null; try { Class clazz = Class.forName("com.zqc.basic.UserServiceImpl"); userService = (UserService) clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } return userService; } }
By de creating objects, the code is still coupled, and com.zqc.basic.UserServiceImpl is coupled in the factory class in the form of string. How to solve this problem?
3.3 improving factory 2 - Reflection and profile creation objects
You need to add a configuration class to the file, write com.zqc.basic.UserServiceImpl in the configuration class, and then modify the factory class.
Create a properties configuration file, which is a special Map in the form of key=value.
File name: applicationContext.properties (placed in the resources directory under Maven project)
userService = com.zqc.basic.UserServiceImpl
Modify factory class:
public class BeanFactory { private static Properties env = new Properties(); // Static code block reading configuration file static { try { // Get IO input stream InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties"); // The file content is encapsulated in the Properties collection to read the file env.load(inputStream); // Close flow } catch (IOException e) { e.printStackTrace(); } } public static UserService getUserService() { UserService userService = null; try { // Get objects through configuration files and reflection Class clazz = Class.forName(env.getProperty("userService")); userService = (UserService) clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } return userService; } }
First, read the configuration file through the stream. Then, get the fully qualified class name by reading userService in the configuration file, and create the object through reflection.
So far, the coupling problem is solved. When we want to change the instantiation method, we only need to modify the configuration file.
4, General plant design
For the above UserService, we created the getUserService() method in the BeanFactory factory class. If OrderService is required, we also need to create getOrderService. If ChartService is required, we also need to create getChartService. In this way, a factory method should be created for each Service, which is very troublesome.
A simple factory needs to create a large number of factory methods, code redundancy, and many parts of these methods are repeated, so these methods need to be transformed into general methods.
Create a common factory code below:
public class BeanFactory { private static Properties env = new Properties(); // Static code block reading configuration file static { try { // Get IO input stream InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties"); // The file content is encapsulated in the Properties collection to read the file env.load(inputStream); // Close flow } catch (IOException e) { e.printStackTrace(); } } public static Object getBean(String key){ Object ret = null; try { // Get objects through configuration files and reflection Class clazz = Class.forName(env.getProperty(key)); ret = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } // Object type is returned return ret; } }
The method of reading the configuration file remains unchanged, except that the type of the created Object is changed to Object, and the type of the returned Object is also Object. When you call getBean to return objects in a program, you need to make strong changes in its type.
Modify test file:
public class MyTest { @Test public void testService() { // Suppose there's a lot of code // create object UserService userService = (UserService) BeanFactory.getBean("userService"); // Call method userService.login("Iron Man", "xxxxx"); // Suppose there's a lot of code } }
5, Summary
- Factory design pattern solves the problem of direct code coupling.
- The common factory mode is solved by configuration file + reflection.
The essence of Spring is a factory: ApplicationContext, which is configured by applicationContext.xml.