工厂模式几乎是我们最常用的一种设计模式了,通过工厂模式可以取代通过new方式这种硬编码的方式创建对象,从而降低代码的耦合性。
耦合性
1. 是指代码之间的强关系,指定是代码间的强关联关系,一方的改变会影响到另一方。
2. 会导致代码不易于移植和维护。
通过工厂模式,我们就可以不用自己使用new显示的去创建对象,创建对象的任务就交给我们的工厂类或者工厂方法来实现。
设计一个业务场景:用户的登录和注册功能。
我们设计一个UserService
接口,UserService
的实现类UserServiceImpl
来实现具体功能,DAO
层也是同理。
在编写Controller
层逻辑时会调用Service
层的实例,而调用Service
层实例时又需要调用DAO
层的实例。如果我们采用直接new这种硬编码的方式会造成代码耦合,所以封装一个工厂类,通过调用工厂类中相关静态方法来获取实例。
这里使用单元测试进行模拟调用过程,直接调用Service
层。
从下面的代码中可以看出,这部分的代码中调用Service
层已经没有耦合性的体现了。
public class UnitTest {
@Test
public void test1() {
// 模拟登录功能,实际上就是通过用户名和密码查询信息
UserService userService = BeanFactory.getUserService();
userService.login("jj", "123456");
// 模拟注册功能,传递一个User对象
User user = new User("Tom", "jerry123");
userService.register(user);
}
}
BeanFactory
中提供创建实例的工厂方法,但是在这部分代码中存在了耦合性,如果有一天我们想要弃用UserSerivceImpl
,改用UserServiceImplNew
来实现相关功能,就不得不修改代码,代码又需要进行编译,这样会麻烦。
public class BeanFactory {
private static UserService getUserService() {
return new UserServiceImpl();
}
private static UserDAO getUserDAO() {
return new UserDAOImpl();
}
}
综上所述,简单工厂方法的设计不能完全解除耦合的状态,所以需要对其进行升级。
反射工厂是对简单工厂的升级,使用Java中反射的特性实现工厂模式。
代码演示:
这里直接演示通过反射的方式编写工厂方法,因为是模拟实现,所以实际结果由控制台进行打印出。
原理:
将实现类的全类名写到配置文件中,如果想要修改实现类只需要修改配置文件即可。
通过Properties
将配置文件中的信息加载
使用反射创建实例
配置文件中的内容:
UserService=com.jc.service.impl.UserServiceImpl
UserDAO=com.jc.dao.impl.UserDAOImpl
public class BeanFactory {
// 定义用于加载配置文件的Properties集合
private static Properties prop = new Properties();
// 因为进行IO操作十分占用资源,所以我们编写一个静态代码块,这样就可以在类加载的过程中就将配置文件中的内容加载到内存中
static {
try {
InputStream steam = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
prop.load(steam);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取UserService实例的方法
*/
public static UserService getUserService() {
String userServiceStr = prop.getProperty("UserService");
UserService userService = null;
try {
userService = (UserService) Class.forName(userServiceStr).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userService;
}
/**
* 获取UserDAO实例的方法
*/
public static UserDAO getUserDAO() {
String UserDAOStr = prop.getProperty("UserDAO");
UserDAO userDAO = null;
try {
userDAO = (UserDAO) Class.forName(UserDAOStr).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return userDAO;
}
}
查询了用户信息: jj, 123456
保存了User信息!
以上代码设计中,如果有一天UserService
的具体实现功能需要改变,使用UserServiceNew
来做它的实现类,这时,只需要修改配置文件中的包路径信息就可以了,而不需要修改代码。
从上面的工厂方法的设计中,每获取一类Bean,都需要设计一个工厂方法。而这些工厂方法产生了冗余,所以我们可以针对这些冗余进行优化。
1. 都是通过Class类的forName方法获取Class类对象
2. 使用Class类对象的newInstance()方法创建对象
public class BeanFactory {
private static Properties prop = new Properties();
static {
try {
InputStream steam = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
prop.load(steam);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String key) {
Object ret = null;
try {
Class clazz = Class.forName(prop.getProperty(key));
ret = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
}
这样设计就可以根据需要的不同,提供不同的参数获取Bean。