解决什么问题
考虑以下现实场景:数据库多种多样,我们可能从Mysql迁移到Oracle,甚至可能从关系型数据库迁移到非关系型数据库。我们不希望业务逻辑依赖具体的数据库实现,否则迁移数据库的时候,我们必须修改核心业务逻辑。怎么解决这个问题呢?依赖倒置,即核心业务逻辑定义数据持久化借口,即DAO接口。我们只要针对每种数据库,写一个DAO实现即可。
那么,我们怎么生成DAO的对象呢?new一个具体的实现类是不合适的,因为这就把代码写死了,下次要换实现,还是得改核心业务逻辑的代码。这里我们需要一个工厂类,它有一个返回DAO对象的方法。我们来考察一下这个工厂类:
- 它一定是被核心逻辑所依赖的;
- 如果它是一个抽象类或一个接口,那么它的实现应该和DAO的实现在一个包里,有相同的提供方;
- 如果它是一个具体类,那么它只能通过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 }