简单工厂的问题
昨天写了简单工厂模式,简单工厂模式虽然做到了对象的创建和使用分离,但是它有个致命的缺陷:不符合开闭原则。每当我们需要新加一个实现类的时候,我们不得不修改工厂的创建方法。
所以现在看来简单工厂模式的工厂责任还是太重,每种实现类都需要由这个工厂来创建,所以每增加一个产品的实现,都需要修改工厂类。
那怎么解决呢?可以想到,继续减轻工厂的责任,每个工厂只负责创建一种产品,这样就引出了今天的主角:工厂方法模式。
工厂方法模式
工厂方法模式对工厂进行抽象,定义了一个抽象工厂,有了抽象工厂就有工厂的实现,并且具体工厂和具体产品一一对应。还是以用户登录为例:
/**
* 产品接口和实现类
*/
public interface UserDao {
void login();
}
class HibernateUserDao implements UserDao {
@Override
public void login() {
System.out.println("Hibernate login");
}
}
class MyBatisUserDao implements UserDao {
@Override
public void login() {
System.out.println("Mybatis login");
}
}
/**
* 抽象工厂和它的实现类
*/
public interface UserDaoFactory {
UserDao getUserDao();
}
class HibernateUserDaoFactory implements UserDaoFactory {
@Override
public UserDao getUserDao() {
return new HibernateUserDao();
}
}
class MybatisUserDaoFactory implements UserDaoFactory {
@Override
public UserDao getUserDao() {
return new MyBatisUserDao();
}
}
然后如果我们需要创建UserDao我们会怎么做呢?
public class Client {
public static void main(String[] args) {
UserDaoFactory factory = new HibernateUserDaoFactory();
UserDao userDao = factory.getUserDao();
userDao.login();
}
}
有些小伙伴可能会问,这样还是不符合开闭原则啊,我们需要不同产品的时候还是需要修改代码。
为了解决这个问题,我们可以使用反射和配置文件
public class PropertiesUtil {
public static Object getBean(String key) {
try(InputStream inputStream = new FileInputStream("src/main/java/create/factoryMethod/application.properties")) {
Properties properties = new Properties();
properties.load(inputStream);
String property = properties.getProperty(key);
Class> clazz = Class.forName(property);
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class Client {
public static void main(String[] args) {
UserDaoFactory factory = (UserDaoFactory) PropertiesUtil.getBean("user-dao-factory");
UserDao userDao = factory.getUserDao();
userDao.login();
}
}
这样,我们无论需要添加怎样的产品,都无需修改原有代码,只需要换配置文件即可。并且针对不同的产品创建过程可以在对应的工厂中进行处理,实现了创建和使用的责任分开。既符合开闭原则也符合单一职责原则。同时,这里的客户端纯粹的面向抽象编程,它无需知道具体地实现类。
餐后甜点
在JDK中典型的使用工厂方法模式:
了解JDK的集合的朋友可能知道,迭代器(这也是一种设计模式--迭代器模式)。那么迭代器是如何创建的呢?
/**
* 把Collection看成抽象工厂,Iterator作为产品
*/
public interface Collection extends Iterable {
Iterator iterator();
}
产品的创建过程是由Collection的具体实现类来实现的:我们以ArrayList为例
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{
public Iterator iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator {
...
}
...
}
可以看到ArrayList作为具体集合定义了一个Itr 的内部类作为Iterator的具体实现类也就是具体产品。
这样用户使用迭代器的时候不用管迭代器的实现是怎样创建的,具体实现是什么样的,直接通过ArrayList这个具体工厂来获取,有些时候我们甚至可以不考虑ArrayList这个具体实现,而是完全面向抽象编程。
在使用中我们可能会这样用,懂了工厂方法模式,我们就能理解迭代器是如何创建的。
List list = userDao.getUsers();
list.iterator();