码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-07-15 01:14:57      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:用户   职责   朋友   cep   random   for   client   bst   creat   

简单工厂的问题

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

工厂方法模式

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

/**
 * 产品接口和实现类
 */
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();

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

标签:用户   职责   朋友   cep   random   for   client   bst   creat   

原文地址:https://www.cnblogs.com/barneycs/p/13303226.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!