工厂模式介绍

我的理解:

  1. 将对象间的引用由进行解耦,由直接new对象改为由第三方对象进行间接引用。例如,在Spring中,service层对dao的引用,尤其是由大量service类的情况下,替换dao层实现(mysqldao->oracledao),相比于直接new对象来说,修改工厂的实现方法可以解耦service层和dao层的耦合且减少大量的工作量。

1.工厂模式的定义

“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。)

2、工厂模式的分类

  • 简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern)。
  • 工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式;
  • 抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。

3、工厂模式的使用

  • Spring中bean的创建
  • 各种连接中,连接对象的获取
  • 日志Logger

4、使用工厂模式的目的

  • 解耦 :把对象的创建和使用的过程分开

    • 与一个对象相关的职责通常有三类:对象本身所具有的职责、创建对象的职责和使用对象的职责。对象本身的职责比较容易理解,就是对象自身所具有的一些数据和行为,可通过一些公开的方法来实现它的职责。通过直接new的方式获取对象,创建对象和使用对象的职责耦合在一起,在修改新的引用时,必须修改类的源代码,违反了“开闭原则”。两个类_A_B之间的关系应该仅仅是_A创建_B或是_A使用_B,而不能两种关系都有。将对象的创建和使用分离,也使得系统更加符合“单一职责原则”,有利于对功能的复用和系统的维护。
  • 降低代码重复: 如果创建某个对象的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。

    • 防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中(比如创建一个中间件连接)
  • 低维护成本 :由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建某个对象的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。

    • Spring

简单工厂模式

工厂模式的一个特殊实现。简单工厂模式在实际中的应用相对于其他2个工厂模式用的还是相对少得多,因为它只适应很多简单的情况。最重要的是它违背了 开放-封闭原则 ,每次添加新的功能都需要修改源代码。(可通过反射解决)

将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。

工厂模式 - 图1

  • 工厂(Factory)角色(根据传入不同参数从而创建不同具体产品类的实例) :简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。

  • 抽象产品(Product)角色(产品的公共接口) :简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。

  • 具体产品(Concrete Product)角色(生产的具体产品):简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

抽象产品)创建一个宠物接口,对于不同的宠物有不同的叫声。

  1. public interface Pet {
  2. public void say();
  3. }

(具体的产品)dog/cat/pig

  1. public class Dog implements Pet {
  2. @Override
  3. public void say() {
  4. System.out.println("wang wang ~");
  5. }
  6. }
  7. public class Cat implements Pet {
  8. @Override
  9. public void say() {
  10. System.out.println("miao miao ~~ ");
  11. }
  12. }
  13. public class Pig implements Pet {
  14. @Override
  15. public void say() {
  16. System.out.println("heng heng ~~");
  17. }
  18. }

(工厂)

  1. public class PetFactory {
  2. public static Pet getPet(String animal){
  3. Pet pet;
  4. switch (animal) {
  5. case "dog": pet = new Dog();
  6. break;
  7. case "cat": pet = new Cat();
  8. break;
  9. case "pig": pet = new Pig();
  10. break;
  11. default:
  12. return null;
  13. }
  14. return pet;
  15. }
  16. }

调用

  1. Pet dog = PetFactory.getPet("dog");
  2. dog.say();
  3. Pet cat = PetFactory.getPet("cat");
  4. cat.say();
  5. Pet pig = PetFactory.getPet("pig");
  6. pig.say();
  7. _______________
  8. wang wang ~
  9. miao miao ~~
  10. heng heng ~~

如果新增一种宠物的话就需要修改getPet()方法,不满足开闭原则

优点:

  • 将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
  • 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。

缺点:

  • 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;

  • 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。

  • 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。

  • 每次新建都需要修改传递参数,在大量类的修改中工作量非常大。

工厂方法模式

在简单工厂模式中只提供一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它需要知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,需要在其中加入必要的业务逻辑,这违背了“开闭原则”。此外,在简单工厂模式中,所有的产品都由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。

在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。工厂方法模式定义如下:

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

工厂模式 - 图2

组成(角色) 关系 作用
抽象产品(Product) 具体产品的父类 描述具体产品的公共接口
具体产品(Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
抽象工厂(Creator) 具体工厂的父类 描述具体工厂的公共接口
具体工厂(Concrete Creator) 抽象工厂的子类;被外界调用 描述具体工厂;实现FactoryMethod工厂方法创建产品的实例

使用步骤

步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下所示:

抽象工厂

  1. public interface PetFactory {
  2. public Pet getPet();
  3. }

在抽象工厂中声明了工厂方法但并未实现工厂方法,具体产品对象的创建由其子类负责,客户端针对抽象工厂编程,可在运行时再指定具体工厂类,具体工厂类实现了工厂方法,不同的具体工厂可以创建不同的具体产品,其典型代码如下所示:

具体工厂类

  1. public class DogFactory implements PetFactory {
  2. @Override
  3. public Pet getPet() {
  4. return new Dog();
  5. }
  6. }
  7. // 在实际使用时,具体工厂类在实现工厂方法时除了创建具体产品对象之外,还可以负责产品对象的初始化工作以及一些资源和环境配置工作,例如连接数据库、创建文件等。
  8. public class CatFactory implements PetFactory {
  9. @Override
  10. public Pet getPet() {
  11. return new Cat();
  12. }
  13. }
  14. public class PigFactory implements PetFactory {
  15. @Override
  16. public Pet getPet() {
  17. return new Pig();
  18. }
  19. }

在客户端代码中,只需关心工厂类即可,不同的具体工厂可以创建不同的产品,典型的客户端类代码片段如下所示:

  1. ……
  2. PetFactory factory;
  3. factory = new CatFactory(); //可以通过配置文件来存储具体工厂类ConcreteFactory的类名,更换新的具体工厂时无须修改源代码,系统扩展更为方便。
  4. product = factory.say();
  5. ……

优点

  • 更符合开-闭原则;新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可

    简单工厂模式需要修改工厂类的判断逻辑

  • 符合单一职责原则 ;每个具体工厂类只负责创建对应的产品

    简单工厂中的工厂类存在复杂的switch逻辑判断

  • 不使用静态工厂方法,可以形成基于继承的等级结构。

    简单工厂模式的工厂类使用静态工厂方法

工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。

缺点:

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
  • 一个具体工厂只能创建一种具体产品

具体使用场景:

  • 当一个类不知道它所需要的对象的类时
    在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
  • 当一个类希望通过其子类来指定创建对象时
    在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

抽象工厂模式

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有唯一性,一般情况下,一个具体工厂中只有一个或者一组重载的工厂方法。但是有时候我们希望一个工厂可以提供多个产品对象,而不是单一的产品对象,如一个电器工厂,它可以生产电视机、电冰箱、空调等多种电器,而不是只生产某一种电器。为了更好地理解抽象工厂模式,我们先引入两个概念;

  • 产品等级结构产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  • 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

工厂模式 - 图3

不同颜色的多个正方形、圆形和椭圆形分别构成了三个不同的产品等级结构,而相同颜色的正方形、圆形和椭圆形构成了一个产品族,每一个形状对象都位于某个产品族,并属于某个产品等级结构。图中一共有五个产品族,分属于三个不同的产品等级结构。我们只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一确定这个产品。

当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。
工厂模式 - 图4

抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。

  1. **_*抽象工厂模式**(Abstract Factory Pattern)**:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为**Kit**模式,它是一种对象创建型模式。*_**

假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。

工厂模式 - 图5

由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理这两个产品族的创建问题,这就是抽象工厂模式。

根据产品角色的结构图,就不难给出工厂角色的结构设计图。

工厂模式 - 图6

每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。

抽象工厂

  1. public interface AbstractFactory {
  2. /**
  3. * 创建CPU对象
  4. * @return CPU对象
  5. */
  6. public Cpu createCpu();
  7. /**
  8. * 创建主板对象
  9. * @return 主板对象
  10. */
  11. public Mainboard createMainboard();
  12. }

Intel工厂

  1. public class IntelFactory implements AbstractFactory {
  2. @Override
  3. public Cpu createCpu() {
  4. // TODO Auto-generated method stub
  5. return new IntelCpu(755);
  6. }
  7. @Override
  8. public Mainboard createMainboard() {
  9. // TODO Auto-generated method stub
  10. return new IntelMainboard(755);
  11. }
  12. }

AMD工厂

  1. public class AmdFactory implements AbstractFactory {
  2. @Override
  3. public Cpu createCpu() {
  4. // TODO Auto-generated method stub
  5. return new IntelCpu(938);
  6. }
  7. @Override
  8. public Mainboard createMainboard() {
  9. // TODO Auto-generated method stub
  10. return new IntelMainboard(938);
  11. }
  12. }

抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。

工厂模式 - 图7

由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。

这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换。

工厂模式 - 图8

抽象工厂模式的优点

  • 分离接口和实现

客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。

  • 使切换产品族变得容易

因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。

抽象工厂模式的缺点

  • 不太容易扩展新的产品

如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。