工厂的建设之路 -- 工厂方法模式

Posted barneycs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了工厂的建设之路 -- 工厂方法模式相关的知识,希望对你有一定的参考价值。

简单工厂的问题

昨天写了简单工厂模式,简单工厂模式虽然做到了对象的创建和使用分离,但是它有个致命的缺陷:不符合开闭原则。每当我们需要新加一个实现类的时候,我们不得不修改工厂的创建方法。
所以现在看来简单工厂模式的工厂责任还是太重,每种实现类都需要由这个工厂来创建,所以每增加一个产品的实现,都需要修改工厂类。
那怎么解决呢?可以想到,继续减轻工厂的责任,每个工厂只负责创建一种产品,这样就引出了今天的主角:工厂方法模式。

工厂方法模式

工厂方法模式对工厂进行抽象,定义了一个抽象工厂,有了抽象工厂就有工厂的实现,并且具体工厂和具体产品一一对应。还是以用户登录为例:

/**
 * 产品接口和实现类
 */
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<E> extends Iterable<E> {
      Iterator<E> iterator();
}

产品的创建过程是由Collection的具体实现类来实现的:我们以ArrayList为例

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
            ...
      }
...
}

可以看到ArrayList作为具体集合定义了一个Itr 的内部类作为Iterator的具体实现类也就是具体产品。
这样用户使用迭代器的时候不用管迭代器的实现是怎样创建的,具体实现是什么样的,直接通过ArrayList这个具体工厂来获取,有些时候我们甚至可以不考虑ArrayList这个具体实现,而是完全面向抽象编程。
在使用中我们可能会这样用,懂了工厂方法模式,我们就能理解迭代器是如何创建的。

      List list = userDao.getUsers();
      list.iterator();






以上是关于工厂的建设之路 -- 工厂方法模式的主要内容,如果未能解决你的问题,请参考以下文章

工厂建设之路-- 简单工厂

菜鸟之路-浅谈设计模式之工厂模式

工程实践之路:C++接口设计中的工厂模型

.Net 设计模式进阶之路——工厂方法模式[Factory Method]

.Net 设计模式进阶之路——工厂方法模式[Factory Method]

行业智能化之三大主要智能工厂建设模式