简单工厂、工厂方法和抽象工厂的总结

首先,放上项目github地址: https://github.com/codethereforam/java-design-patterns, 我是用java实现的html

1、前言

题目中的这三个设计模式属于建立型模式,做用是为了抽象实例化过程java

我以前学过这三个设计模式,但最近发现又没法厘清这三个的区别了,为了不下次又忘了,因而想动手记录下来。mysql

可能有同窗有疑问,提早说一下,下面所展现的类图IDEA自带插件UML Support自动生成,而时序图由插件SequencePlugin自动生成。若是有同窗对类图和时序图还不了解,请先google自学一下。git

下面我结合模拟场景总结一下这三个模式,具体代码请点击本文开头的github连接。github

2、简单工厂

模拟场景:一个用户管理系统,假设只有一张User表。原本用的mysql,但需求忽然发生变化,如今要用Oralce,因为这两个数据库的SQL语句有些差异,须要重写数据库层面的代码,如今要求系统能够灵活切换数据库。sql

关键代码:数据库

public class UserDAOFactory {
    //静态工厂方法
    public static UserDAO createUserDAO(String database) {
        UserDAO userDAO = null;
        switch (database) {
            case "mysql":
                userDAO = new UserDAOMysqlImpl();
                break;
            case "oracle":
                userDAO = new UserDAOOracleImpl();
                break;
            default:
        }
        return userDAO;
    }
}

分析:若是如今要改用SQL server数据库,须要添加一个UserDAOSqlserverImpl,而后在UserDAOFactory类的createUserDAO方法中添加一个case,这显然违背了开闭原则设计模式

  • 特色
    • 工厂类包含必要的逻辑判断来选择生产具体产品
    • 用于生产单个产品
  • 优势
    • 去除客户端与具体产品的依赖
  • 缺点
    • 添加产品须要修改工厂类,违背开闭原则
  • 角色
    • 抽象产品(UserDAO)
    • 具体产品(UserDAOMysqlImpl & UserDAOOracleImpl)
    • 工厂(UserDAOFactory)

3、工厂方法

模拟场景:和上述简单工厂模拟场景同样oracle

分析:若是如今要改用SQL server数据库,则需添加一个UserDAOSqlserverImpl和相应的工厂UserDAOFactorySqlserverImpl,再更改Main中的实例化代码,这知足了开闭原则,扩展很方便。但若是支持的数据库一多,那工厂就会泛滥。google

  • 特色
    • 一个产品对应一个工厂类
    • 用于生产某种类型产品
  • 优势
    • 方便添加新产品
    • 添加新产品只需添加相应工厂类,符合开闭原则
  • 缺点
    • 产品多时,工厂泛滥
  • 角色
    • 抽象产品(UserDAO)
    • 具体产品(UserDAOMysqlImpl & UserDAOOracleImpl)
    • 抽象工厂(UserDAOFactory)
    • 具体工厂(UserDAOFactoryMysqlImpl & UserDAOFactoryOracleImpl)

4、抽象工厂

模拟场景:在以前的场景基础上,若是系统原本还有一个日志表,是用来记录日志的。

分析:若是如今要改用SQL server数据库,则需添加UserDAOSqlserverImplLogDAOSqlserverImplDAOFactorySqlserverImpl,再更改Main中的实例化代码。但若是如今要添加一个其余的表,那么就要改DAOFactory接口和接口中方法的实现,要改动的地方太多。

  • 特色
    • 用于生产一系列产品
  • 优势
    • 易于改变工厂生产行为,产生新的产品系列
    • 具体建立过程与客户端分离,客户端经过接口操纵实例(factory1.createUserDAO().add())
  • 缺点
    • 添加新产品,要修改抽象工厂接口、具体工厂,改动太多
  • 角色
    • 抽象产品(UserDAO & LogDAO)
    • 具体产品(UserDAOMysqlImpl & UserDAOOracleImpl & LogDAOMysqlImpl & LogDAOOracleImpl)
    • 抽象工厂(DAOFactory)
    • 具体工厂(DAOFactoryMysqlImpl & DAOFactoryOracleImpl)

5、用简单工厂改进抽象工厂

关键代码(选取DataAccess):

public UserDAO createUserDAO() {
        UserDAO userDAO = null;
        switch (database) {
            case MYSQL:
                userDAO = new UserDAOMysqlImpl();
                break;
            case ORACLE:
                userDAO = new UserDAOOracleImpl();
                break;
            default:
        }
        return userDAO;
}

分析:与抽象工厂相比,该方法减小了三个类,添加了一个DataAccess类,类的数量减小了,系统复杂性下降。若是如今要改用SQL server数据库,则需添加UserDAOSqlserverImplLogDAOSqlserverImpl,而后在DataAccess类中的createUserDAO方法和createLogDAO方法分别添加一个case,这违背了开闭原则。

  • 特色
    • 用DataAccess取代抽象工厂和具体工厂
    • DataAccess经过判断控制生产行为
  • 优势
    • 减小类
  • 缺点
    • 添加新产品系列,要改动DataAccess中的switch-case

6、用反射改进抽象工厂

关键代码(选取DataAccess):

public static final String PACKAGE_NAME = DataAccess.class.getPackage().getName();

public UserDAO createUserDAO() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String className = PACKAGE_NAME + ".UserDAO" + database + "Impl";
        return (UserDAO) Class.forName(className).newInstance();
}

分析:若是要换数据库,则无需修改DataAccess类中的代码。若是要添加表,只须要添加一个抽象产品接口和两个具体产品实现,而后在DataAccess中添加一个create**方法,扩展起来很是方便。

  • 优势
    • 减小类
    • 解决抽象工厂添加产品改动较多的问题,方便扩展
  • 可以使用配置文件继续完善

7、总结

若是你仔细看到这,你可能会以为我例子举的不恰当,哪里有系统只有一个表的呢,前面的场景直接考虑抽象工厂就好了。我认可我举的例子有问题,以前写代码时没有发现,应该是当时理解的还不够深刻。

简单工厂和工厂方法的模拟场景应该改成:系统原本有一直表,但如今要添加表,而不是换数据库。而抽象工厂的模拟场景应该改成:在上述的基础上要换数据库。若是你理解了三个模式,我想这两个模拟场景你应该也知道怎么实现了。

本文的例子我参考了大话设计模式,但其余代码和文字是我本身的理解。若是有错误,望各位不吝赐教,在评论区指出。

8、参考资料

相关文章
相关标签/搜索