介绍
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。它的主要作用就是用来生产对象,将对象的创建和使用分开,降低了代码之间的耦合性。下面就以一些简单的代码来浅析工厂模式,加深对工厂模式的理解。
代码引入
下面以常见的用户注册登录代码为例。
public interface UserDao {
//注册
public void register();
//登录
public void login();
}
userDaoImpl.java
public class UserDaoImpl implements UserDao{
@Override
public void register() {
System.out.println("insert user into table");
}
@Override
public void login() {
System.out.println("select username,pwd from table");
}
}
userService.java
public interface UserService {
public void register();
public void login();
}
userServiceImpl.java
public class UserServiceImpl implements UserService{
private UserDao userDao = new UserDaoImpl();
@Override
public void register() {
userDao.register();
}
@Override
public void login() {
userDao.login();
}
}
Test.java
public class Test {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
service.register();
service.login();
}
}
运行结果:
insert user into table
select username,pwd from table
分析
上述代码在功能实现上没有什么问题,但存在耦合性,就是对象的创建和使用耦合在了一起。至于为什么要使用工厂模式解耦,可以看看这篇博客为什么要用工厂模式,我觉得讲的不错。这里主要讲解代码实现工厂模式。那接下来,我们将上述代码一步步改造成工厂模式。
创建工厂类
既然我们要把对象的创建和使用分开,我们自然会想到创建一个工厂类。于是我们可以创建一个BeanFactory类,用于生产对象。
BeanFactory.java
public class BeanFactory {
public static UserService getUserService(){
return new UserServiceImpl();
}
}
我们在工厂类里面定义了一个生产UserService类的方法,然后我们在主函数中通过BeanFactory调用此方法创建对象。
public class Test {
public static void main(String[] args) {
//UserService service = new UserServiceImpl();
UserService service = BeanFactory.getUserService();
service.register();
service.login();
}
}
最终结果是一样的:
insert user into table
select username,pwd from table
在上述代码中,我们不再直接通过new关键字创建对象,而是交给了BeanFactory类,让这个类创建我们需要的对象,实现了对象的创建和使用分离。不过,我们还是在工厂类的方法里new了对象,那么我们接下来可以对工厂类中创建对象的方法再进行改造。
使用反射
学过反射应该知道,我们还可以通过反射创建对象,降低耦合;于是改造如下:
public class BeanFactory {
public static UserService getUserService(){
//return new UserServiceImpl();
UserService service = null;
try {
Class clas = Class.forName("com.qiming.spring.service.UserServiceImpl");
//创建对象
service = (UserService) clas.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return service;
}
}
上述代码改造后,我们只要输入要创建的对象的全限定类名,然后通过反射创建对象。
简单工厂模式
上述代码中全限定类名作为一个字符串,还是需要我们在代码中写。那我们不妨将这个类名写在配置文件中,然后让代码读取配置文件,实现读写分离,进一步降低耦合。
创建一个配置文件 applicationContext.properties:
userService = com.qiming.spring.service.UserServiceImpl
改造工厂类:
public class BeanFactory {
/**
* 通过properties类获取配置文件中的配置
*/
private static Properties properties = new Properties();
static {
try {
//1. 获得IO输入流
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.properties");
//2. 加载流
properties.load(inputStream);
//3. 关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService(){
//return new UserServiceImpl();
UserService service = null;
try {
//Class clas = Class.forName("com.qiming.spring.service.UserServiceImpl");
Class clas = Class.forName(properties.getProperty("userService"));
service = (UserService) clas.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return service;
}
}
改造后,我们只需要修改配置文件里面的内容就可以修改对象,实现了读写分离。例如我们不再使用userServiceImpl对象时,而是使用userServiceNew时,我们只需要在配置文件中修改全限定类名即可:
userService = com.qiming.spring.service.UserServiceNew
1
这样极大降低了代码间的耦合,这就是一个简单的工厂模式。
通用工厂模式
简单工厂模式极大降低了代码间的耦合,但实际中创建的对象不止一种。当我们要创建UserDao对象时,我们要写public static UserDao getUserDao()方法;要创建TextService对象时,要写public static TextService getTextService()方法…而且我们发现这些代码实现的过程基本是一样的,内容上具有重复性。所以,我们还可以继续改造工厂类里面的方法,用一个方法创建任意的对象。
public static Object getBean(String key){
Object obj = null;
try {
Class cls = Class.forName(properties.getProperty(key));
obj = cls.newInstance();
}catch (Exception e){
e.printStackTrace();
}
return obj;
}
当我们需要创建对象时,如下即可:
//创建UserService对象
UserService service = (UserService)BeanFactory.getBean("userService");
//创建UserDao对象
UserDao userDao = (UserDao)BeanFactory.getBean("userDao");
//创建TextService对象
TextService textService = (TextService)BeanFactory.getBean("textService");
这就是一个通用工厂模式。
总结
通过上述一步步改造,我们实现了工厂模式。可以发现,我们写的通用工厂类有spring的内味了。没错,spring中的工厂类的实现思想和我们写的是差不多的,如spring中通过ApplicationContext获取容器对象,通过getBean读取创建对象… …spring可谓是工厂模式的设计典范,值得深入研究。