简单工厂
1. 使用场景
属于设计模式中的“生成器”模式,一般用来封装对象的创建过程,如果对象创建过程比较复杂,或者对于对象的创建有特殊需求(例如控制对象的创建个数等),都可以通过工厂模式来解决。同时应用工厂模式和接口可以用来来降低对象之间的耦合。
2. 实现举例
比如在设计Dao时,客户需要使用MySQL数据库,因此我们提供了UserDaoJdbc的实现类。但当我们的的项目卖给另一位客户时,客户要求上MongoDB数据库,这时又需要提供另一个UserDaoMongoDB的实现类。
如果不提前做出规划设计,直接在代码中引用了UserDaoJdbc的代码,那么需要改变实现类时,所有引用UserDaoJdbc的地方全部需要进行修改替换。一句话,可扩展性差,不灵活。
如何解决呢?要点是面向接口编程,并且把Dao对象的创建过程分离出去,交给工厂类来完成
1) 接口和实现代码:
interface UserDao {
void save(User user);
void update(User user);
void delete(int id);
User findById(int id);
List<User> findAll();
}
class UserDaoJdbc implements UserDao {
// 具体实现略
}
class UserDaoMongoDB implements UserDao {
// 具体实现略
}
2) 配置文件(config.properties) :
interview.UserDao=interview.UserDaoMongoDB
3) 工厂类
class DaoFactory {
static Map<Class<?>, Object> map = new HashMap<>();
static {
try (InputStream is = DaoFactory.class.getResourceAsStream("config.properties")){
// 读取配置文件
Properties config = new Properties();
config.load(is);
Enumeration<Object> e = config.keys();
while(e.hasMoreElements()) {
// 获取接口名
String interfaceName = e.nextElement().toString();
// 获取实现类名
String implementClassName = config.getProperty(interfaceName);
Class<?> interfaceClass = Class.forName(interfaceName);
Class<?> implementClass = Class.forName(implementClassName);
// 将接口.class作为key,实现类的实例对象作为值存入map
map.put(interfaceClass, implementClass.newInstance());
}
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new Error(e);
}
}
// 根据接口.class 获取实现类的实例对象
static <T> T getBean(Class<T> interfaceClass) {
return interfaceClass.cast(map.get(interfaceClass));
}
}
4) 调用代码
// 调用代码,只需要知道工厂和接口,无需知道(依赖于)实现类
UserDao userDao = DaoFactory.getBean(UserDao.class);
要点
- 接口需要有
- 将依赖关系定义放入一个配置文件
- 工厂负责读取配置文件,根据配置选择实现类,创建对象实例
3. 不使用理由
工厂在设计时需要考虑很多事情:对象在创建时需不需要单例?需不需要延迟实例化?加不加控制反转?
现代的IOC容器(例如spring),已经充分考虑并实现了以上问题,更为完善和强大,因此推荐直接使用IOC容器。