1. 抽象工厂模式(Abstract Factory Pattern)
Provie an interface for creating families of related or dependent objects without specifying theiur concrete classes. (为创建一组相关或相互依赖的对象提供一个接口, 而无须指定它们的实现类)
1.1 抽象工厂模式-核心思想
- 对于一组相关联的对象,设置统一的生成方式
- 抽象工厂模式中在切换实现类时,切换的是一组产品的实现方式,而不是单个的实现方式.
1.2 抽象工厂模式-类图
该设计模式类图包含两个维度: 产品族(序号1和序号2), 产品类型(A和B)
- AbstractProductA/AbsProductB: 两个产品家族的抽象类
- ProductA1/ProductA2/ProductB1/ProductB2: 具体的产品类
- AbstractFactory: 抽象工厂类, 定义产品族的抽象创建方法. 当有N个产品类型时, 就应该有N个抽象方法
- ConcreteFactory1/ConcreteFactory2: 具体产品族的工厂实现类,一个产品族对应一个工厂实现类.
1.3 抽象工厂模式-优缺点
- 产品族扩展容易: 比如新增一产品族3, 那么只需要创建ConcreteFactory3,并实现其相关方法即可
- 产品族扩展难: 比如新增一产品类型C, 那么需要在顶层的AbstractFactory中添加方法,并在所有已实现的Factory中也添加方法。改动较大.
1.4 抽象工厂模式适用场景
2. 抽象工厂模式举例
我们还借用工厂方法设计模式的例子: 一个应用需要同时支持在oracle 和mysql 环境中运行. 我们知道一个项目中会有很多中dao, 我们需要为每一种dao 都提供oracle 和mysql 的实现.
- 产品族: oracle 家族和mysql 家族
- 产品类型: 不同的Dao, 如EmpDao 和 DeptDao
2.1 类图
[外链图片转存失败(img-quoWxFEI-1566894786789)(https://raw.githubusercontent.com/zongf0504/blog-images/master/images/design-parttern/ldp-absfactory-02.png)]
2.2 抽象工厂类
顶层抽象工厂类必须提供每一种产品类型的创建方法
public interface IDaoFactory {
IEmpDao getEmpDao();
IDeptDao getDeptDao();
}
2.3 Oracle 工厂类
public class DaoFactoryForOracle implements IDaoFactory {
@Override
public IEmpDao getEmpDao() {
return new EmpDaoForOracle();
}
@Override
public IDeptDao getDeptDao() {
return new DeptDaoForOracle();
}
}
2.4 Mysql 工厂类
public class DaoFactoryForMysql implements IDaoFactory {
@Override
public IEmpDao getEmpDao() {
return new EmpDaoForMysql();
}
@Override
public IDeptDao getDeptDao() {
return new DeptDaoForMysql();
}
}
2.4 抽象产品类型
笔者为IEmpDao 和 IDeptDao 类似, 均只定义了save和delete方法, 笔者只提供一个类的源代码
public interface IEmpDao {
void save();
void delete();
}
2.5 具体产品实现类
EmpDaoForMysql, EmpDaoForOracle, DeptDaoForMysql, DeptDaoForOracle 四个实现类, 实现代码类似,笔者仅提供一个类的代码
public class EmpDaoForMysql implements IEmpDao {
@Override
public void save() {
System.out.println("mysql-保存 emp 对象");
}
@Override
public void delete() {
System.out.println("mysql-删除 emp 对象");
}
}
2.6 测试
- 在切换数据源时, 仅仅需要修改一处代码即可
@Test
public void test_oracle(){
IDaoFactory daoFactory = new DaoFactoryForOracle();
IEmpDao empDao = daoFactory.getEmpDao();
IDeptDao deptDao = daoFactory.getDeptDao();
Assert.assertEquals(EmpDaoForOracle.class, empDao.getClass());
Assert.assertEquals(DeptDaoForOracle.class, deptDao.getClass());
}
@Test
public void test_mysql(){
IDaoFactory daoFactory = new DaoFactoryForMysql();
IEmpDao empDao = daoFactory.getEmpDao();
IDeptDao deptDao = daoFactory.getDeptDao();
Assert.assertEquals(EmpDaoForMysql.class, empDao.getClass());
Assert.assertEquals(DeptDaoForMysql.class, deptDao.getClass());
}
3. 总结
笔者给的这个例子,也可以对EmpDao 和 DeptDao 分别使用工厂方法模式, 但是这样的话, 客户端便需要和两个工厂抽象类进行耦合, 当需要切换环境时, 需要修改每一处获取工厂方法的地方.
[外链图片转存失败(img-TEGvYiIh-1566894786790)(https://raw.githubusercontent.com/zongf0504/blog-images/master/images/design-parttern/ldp-absfactory-03.png)]