工厂模式
什么是工厂设计模式
1. 概念:通过工厂类创建对象
2. 好处:解耦合
3. 耦合:代码之间产生强关联,修改一方会影响到另一方
解耦的本质在于隔离变化
耦合与解耦合本质在于对变化的处理。
通常如果将接口的实现类硬编码在程序中,就会使得变化散落在程序各处,使得修改会产生连锁反应。
但代码不能完全解耦,如果代码间完全没有关联那程序也就无法正常运行。
所以,解耦的本质在于:隔离变化。
分析这段代码:
UserService userService = new UserServiceImpl();
首先在等号的左侧,我们通过面向接口编程的方式将变化隔离到右侧的实现类部分,但是右侧仍然存在耦合关系,如何将右侧的new UserServiceImpl()也进行解耦呢?
对象的创建方式
讨论对象实现类的解耦之前,首先要了解对象的创建方式。
对象创建方式有两种:
- 直接调用构造方法创建对象,即new的方式。
UserService userService = new UserServiceImpl();
- 通过反射的形式创建对象。
Class clazz = Class.forName("com.peter.UserServiceImpl");
UserService userService = (UserService)clazz.newInstance();
显然,如果通过第一种方式直接构造对象需要首先导入该实现类,其次要通过new语句硬编码类对象的创建,那么必然带来耦合。而通过反射的形式创建对象,则可以将类的变化转变为一个字符串,而对于字符串而言又可以将其隔离到配置文件进行读取,这样我们的代码就不需要跟随实现类的变化而做很大的调整。
我们创建一种通过反射的形式创建对象的工具类,通过它来完成对象的创建,这种模式叫做工厂模式,这个工具类叫做反射工厂。
简单工厂和通用工厂
知道了工厂的基本原理,大体上我们可以进行相应的实现。比如我们要创建一个方法用于实现UserService对象,那么可以这样做:
- 定义接口
public interface UserService {
void getUser();
}
- 定义实现类
public class UserServiceImpl implements UserService {
public void getUser() {
System.out.println("get a user");
}
}
- 定义配置文件
# applicationContext.properties
userService = com.peter.UserServiceImpl
- 定义工厂
public class BeanFactory {
private static Properties env = new Properties();
static {
try {
InputStream config = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
env.load(config);
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService() {
UserService userService = null;
try {
Class clazz = Class.forName(env.getProperty("userService"));
userService = (UserService)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return userService;
}
- 调用工厂
public class Main {
public static void main(String[] args) {
// 简单工厂
UserService userService = BeanFactory.getUserService();
userService.getUser();
}
}
在第四步定义工厂时,我们定义了getUserService方法用于获取UserService接口类型的对象,这种针对某一类型单独定义方法的工厂叫做简单工厂。但是,在简单工厂中,很多代码实际上都是样板代码,如果我们需要添加一个类型则需要重复编写这些代码,于是我们想到要进一步抽象使其更加通用。
public static Object getBean(String key) {
Object ret = null;
try {
Class clazz = Class.forName(env.getProperty(key));
ret = clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return ret;
}
我们将原先与UserService相关的部分抽象为Object和入参key,这样我们就实现了一个通用的类对象创建方式,实现了这种方法的工厂叫做通用工厂。
我们看如何调用通用工厂:
public class Main {
public static void main(String[] args) {
// 通用工厂
UserService userService = (UserService)BeanFactory.getBean("userService");
userService.getUser();
}
}
到此为止,我们使用通用工厂+配置文件的方式将变化隔离到了配置文件,保证了代码的解耦合。
总结
代码解耦由三个重要的部分组成:接口、工厂、配置文件。
通过接口和工厂实现代码逻辑的组装,通过配置文件实现了变化的隔离和实现类的注入。我们可能觉得这和Spring的设计思想是一致的,其实Spring中IOC容器的本质就是这么一个通用工厂,名字叫做ApplicationContext,而相应的配置文件叫做applicationContext.xml。
源代码: https://github.com/PeterWangYong/blog-code/tree/master/factory