04. 23种经典设计模式-14-抽象工厂模式

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: 具体产品族的工厂实现类,一个产品族对应一个工厂实现类.

image

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)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值