Java工厂模式


1. 工厂模式简介

工厂模式(Factory Pattern):这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,在创建对象的时候不会对客户端暴露创建的逻辑。

工厂模式的解决的问题是:当目前的类的构造方法是很长一段的时候,可以将构造工作抽离出来,将其责任委托到工厂上

面向对象的封装和分派提供如下思路:尽量将长的代码分派切割成每段,将每段再封装起来,减少段与段之间的耦合性。这样就会将风险分散,以后如果需要修改,只需要修改每段即可。

2. 简单工厂模式入门

简单工厂模式需要将创建实例的工作使用示例的工作分开,也就是说,让创建实例大量初始化工作的从实例的构造方法中抽离出去,这时候就需要Factory工厂模式来生成对象了。

简单工厂模式不是23种里面的一种,表现为有一个专门生产某个产品的类

2.1 工厂模式的角色划分

工厂角色:是简单工厂模式的核心,由它创建的所有的类内部逻辑。当然工厂类必须能够被外界所调用,创建所需要的产品对象。一般而言,工厂类提供一个静态方法,外部程序通过该方法创建所需要的对象

抽象产品角色:简单工厂模式所创建的是所有对象的父类,注意,这里的父类可以是接口,也可以是抽象类,它负责描述所创建实例共有的公共接口

具体产品角色:简单工厂所创建的具体实例对象,这些具体的产品往往具有相同的父类

2.2 简单工厂模式的优点

  • 工厂类是整个简单工厂模式的关键所在,它包含必要的判断逻辑,能够根据外部所给定的信息(配置、参数),决定究竟应该创建哪个具体类的对象,用户在使用使的时候可以直接根据工厂类去创建所需要的实例,而不需要了解这些对象是如何创建以及如何组织的。
  • 通过引入配置文件以及反射,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性
  • 客户端不需要知道所创建的具体产品的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,可以通过简单工厂模式来减少使用者的记忆量

2.3 简单工厂模式的缺点

由于工厂类中集中了所有实例的创建逻辑,一旦这个工厂出了问题,需要修改,那么所有的客户端都会受到牵连。

由于简单工厂模式的产品是基于一个共同的抽象类或者接口,这样一来,在产品的种类增加的时候,即有不同的产品接口或者抽象类的时候,工厂类就需要判断何时创建何种接口的产品,这就和创建何种种类的产品相互混淆了,违背了单一职责的原则。

违背了开放-关闭原则,当需要增加一个产品的时候修改工厂类,响应的工厂类就需要重新编译一遍,但这一点可以通过反射来解决,这仅限于产品类的构造以及初始化相同的场景,对于各产品实例化或者初始化不同的场景,比较难利用反射来满足开放-关闭原则

3. 工厂方法模式

3.1 工厂方法模式简介

工厂方法模式:解决了简单工厂模式的问题,被叫做多态工厂模式或者虚拟构造器模式,在工厂方法模式中:

  • 工厂父类定义创建产品对象的公共接口,具体的工厂子类创建具体的产品对象
  • 每一个工厂子类负责创建一种具体的产品

3.2 工厂方法模式角色划分

  • 抽象产品(或者产品接口)

  • 具体产品

  • 工厂接口

  • 具体工厂

3.3 工厂方法模式的使用方法

与简单工厂模式直接使用静态工厂方法来创建工厂对象不同,在工厂方法中,客户端通过实例化具体的工厂类,并调用其创建实例接口创建具体的产品类的实例。根据依赖倒置原则,具体工厂类的实例由工厂接口引用(客户端依赖于抽象工厂而非具体工厂),具体产品实例由产品接口来引用(客户端和工厂依赖于抽象产品而非具体产品)。

3.4 工厂方式中的优点

  • 因为每个具体工厂只负责创建产品,没有简单工厂中的逻辑判断,因此符合单一职责原则。
  • 与简单工厂模式不同,只需要增加响应的具体产品类和相应的工厂子类即可。相比于简单工厂模式需要修改判断逻辑而言,工厂方法更符合开闭原则

3.5 工厂方法模式的缺点

添加新产品的时候,除了添加新产品,还要提高与之对应的具体工厂类,系统类的个数将会成对增加,在一定程度上增加了系统的复杂,有更多的类需要编译和运行。

虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要换用另外一种产品,仍然需要修改实例化的具体工厂

一个具体工厂只能够创建一种具体产品

4. 抽象工厂模式

在工厂方法模式中一种工厂只能创建一种具体的产品,而在抽象工厂模式中,一种具体工厂可以创建多个种类的具体产品

4.1 抽象工厂模式简介

抽象工厂模式中,抽象工厂提供一系列创建多个抽象产品的接口,而具体的工厂负责实现具体的产品实例。抽象工厂模式与工厂方法模式最大的区别在于每个工厂可以创建多个种类的产品

4.2 抽象工厂模式角色划分

  • 抽象产品(产品接口)
  • 具体产品
  • 抽象工厂
  • 具体工厂
  • 产品族

4.3 抽象工厂模式的使用方法

与工厂模式类似,在创建具体产品的时候,客户端通过实例化具体的工厂类,并调用创建目标产品的方法创建具体的产品类的实例,根据依赖倒置的原则,具体的工厂类的实例由工厂接口引用。具体产品的实例由产品接口引用

4.4 抽象工厂模式案例解析

public class Client {

  public static void main(String[] args) {
    IDaoFactory factory = new MySQLDaoFactory();

    IUserDao userDao = factory.createUserDao();
    User user = new User();
    user.setUsername("demo");
    user.setPassword("demo".toCharArray());
    userDao.addUser(user);

    IRoleDao roleDao = factory.createRoleDao();
    roleDao.getRole("admin");

    IProductDao productDao = factory.createProductDao();
    Product product = new Product();
    productDao.removeProduct(product);

  }

}

在实际的项目开发过程中,经常要求使用其它类型的数据库,而不希望过多修改已有的代码。因此,需要为每种DAO创建一个DAO接口,同时为不同数据库实现相应的具体类

  • 调用方依赖于DAO接口而非具体实现(依赖倒置原则),因此切换数据库的时候,调用方法代码是不需要修改的

这些具体的DAO实现类往往不由调用方实例化,从而实现具体DAO的使用方法与DAO的构建解耦。实际上,这些DAO类一般由对应的具体工厂类进行构建。调用方不依赖于具体工厂而是抽象工厂。

每种具体工厂都能创建多种产品,由同一种工厂创建的产品属于同一产品族。

切换数据库也就是切换产品族,只需要切换具体的工厂类。

4.5 抽象工厂模式优点

因为每个具体工厂类只负责创建产品,因此没有了简单工厂中的逻辑判断,因此符合单一职责的原则

与简单工厂模式不同,抽象工厂并不使用静态工厂方法,可以形成基于继承的等级结构

新增一个产品族时,只需要增加相应的具体产品和对应的具体工厂类即可,相比于简单工厂模式需要修改逻辑判断。

4.6 抽象工厂模式缺点

新增产品种类的时候,需要修改工厂接口(或者抽象工厂)以及所有具体工厂。

抽象工厂模式对于旧的产品族符合开闭原则,而新的话就不符合了,这一特性被称为开闭原则的倾斜性。


文章作者: 穿山甲
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 穿山甲 !
  目录