Abstract Factory

Posted johnsblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Abstract Factory相关的知识,希望对你有一定的参考价值。

解决什么问题

  考虑以下现实场景:数据库多种多样,我们可能从mysql迁移到Oracle,甚至可能从关系型数据库迁移到非关系型数据库。我们不希望业务逻辑依赖具体的数据库实现,否则迁移数据库的时候,我们必须修改核心业务逻辑。怎么解决这个问题呢?依赖倒置,即核心业务逻辑定义数据持久化借口,即DAO接口。我们只要针对每种数据库,写一个DAO实现即可。

  技术图片

  那么,我们怎么生成DAO的对象呢?new一个具体的实现类是不合适的,因为这就把代码写死了,下次要换实现,还是得改核心业务逻辑的代码。这里我们需要一个工厂类,它有一个返回DAO对象的方法。我们来考察一下这个工厂类:

  1. 它一定是被核心逻辑所依赖的;
  2. 如果它是一个抽象类或一个接口,那么它的实现应该和DAO的实现在一个包里,有相同的提供方;
  3. 如果它是一个具体类,那么它只能通过classLoader来装载具体的DAO实现。因为DAO的实现依赖核心逻辑,而核心逻辑依赖工厂类,因此工厂类一定不能依赖DAO的实现,否则就循环依赖了;

  好了。问题到这里讨论结束。上面的2,实际上就是Abstract Factory,它解决的问题,实际上是多态的类(DAO)对象的创建,让多态类(DAO) 的使用方(核心业务逻辑),不必依赖于具体的类实现(Mysql DAO, Oracle DAO, ...),从而和具体的实现解耦。

代码(Java)

核心业务逻辑

 1 package com.mycompany.business;
 2 
 3 public class Business {
 4 
 5     /**
 6      * 之所以参数不是IDAO,而是factory,是因为factory是产生一组紧密相关对象的工厂
 7      * 假设这里除了IDAO之外,还有I1XXX, I2XXX,它们是一组相关的接口,它们都由工厂生产
 8      * 如果要把IDAO做参数,则I1XXX, I2XXX也得做参数
 9      * 随着相关的接口越来越多,参数也越来越多,这是不合适的。
10      * @param daoFactory
11      */
12     public void process(DAOFactory daoFactory){
13         BusinessEntity businessEntity = new BusinessEntity();
14         businessEntity.setId(1L);
15         IDAO dao = daoFactory.generateDAO();
16         dao.insert(businessEntity);
17         dao.delete(businessEntity.getId());
18     }
19 }
1 package com.mycompany.business;
2 
3 public interface DAOFactory {
4     IDAO generateDAO();
5 }
1 package com.mycompany.business;
2 
3 public interface IDAO {
4     int insert(BusinessEntity businessEntity);
5     int delete(Long id);
6 }

基础设施

mysql实现

 1 package com.mycompany.infrastructure.mysql;
 2 
 3 import com.mycompany.business.DAOFactory;
 4 import com.mycompany.business.IDAO;
 5 
 6 public class MysqlDAOFactory implements DAOFactory {
 7     public IDAO generateDAO() {
 8         return new MysqlDAOImpl();
 9     }
10 }
 1 package com.mycompany.infrastructure.mysql;
 2 
 3 import com.mycompany.business.BusinessEntity;
 4 import com.mycompany.business.IDAO;
 5 
 6 public class MysqlDAOImpl implements IDAO {
 7     public int insert(BusinessEntity businessEntity) {
 8         System.out.println("MysqlDAOImpl insert ...");
 9         return 0;
10     }
11 
12     public int delete(Long id) {
13         System.out.println("MysqlDAOImpl delete ...");
14         return 0;
15     }
16 }

oracle实现

 1 package com.mycompany.infrastructure.oracle;
 2 
 3 import com.mycompany.business.DAOFactory;
 4 import com.mycompany.business.IDAO;
 5 
 6 public class OracleDAOFactory implements DAOFactory {
 7     public IDAO generateDAO() {
 8         return new OracleDAOImpl();
 9     }
10 }
 1 package com.mycompany.infrastructure.oracle;
 2 
 3 import com.mycompany.business.BusinessEntity;
 4 import com.mycompany.business.IDAO;
 5 
 6 public class OracleDAOImpl implements IDAO {
 7     public int insert(BusinessEntity businessEntity) {
 8         System.out.println("OracleDAOImpl insert ...");
 9         return 0;
10     }
11 
12     public int delete(Long id) {
13         System.out.println("OracleDAOImpl delete ...");
14         return 0;
15     }
16 }

进一步思考

回到对工厂类的考察,那么3是什么呢?按我的理解,3就是经典的IoC,即控制反转。

一个DAO,用不着我们自己new,而是由一个工厂产生,并且这个工厂不依赖具体的DAO实现,因此它只能通过classLoader来装载这个类。

 1 package com.mycompany.business;
 2 
 3 public class DAOFactory2 {
 4     public IDAO generateDAO(String className)  {
 5         try{
 6             Class<?> daoClass = Thread.currentThread().getContextClassLoader().loadClass(className);
 7             return (IDAO) daoClass.newInstance();
 8         }catch (Exception e){
 9             throw new RuntimeException("generateDAO error", e);
10         }
11 
12     }
13 }

 

以上是关于Abstract Factory的主要内容,如果未能解决你的问题,请参考以下文章

抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式(Abstract Factory Pattern)

DESIGN PATTERNS - ABSTRACT FACTORY PATTERN

DESIGN PATTERNS - ABSTRACT FACTORY PATTERN

抽象工厂模式-Abstract Factory

抽象工厂(Abstract Factory)