UP | HOME

设计模式之工厂模式

目录

1 前言

在多次接触设计模式的过程中,工厂模式留给我的印象是最深的,因为,它基本上是各种教程前面必然会出现的一个模式 (¬_¬)

所以,我觉得还是有必要总结一下和工厂模式有关的内容。

2 三个面向对象设计原则

某种程度上,设计模式的设计依据便是面向对象的设计原则,这里先列出来个人认为有助于工厂模式的理解的三个原则:

  • 开闭原则:类应该对扩展开发,对修改关闭。也就是说,我们应该在尽量避免修改已有代码的情况下扩展增加功能
  • 依赖倒置原则:依赖抽象,不要依赖具体类。也就是说,我们应该避免对具体类的依赖,而要尽量的依赖抽象
  • 单一职责原则:类的职责要单一,不要将太多的职责放在一个类中

3 简单工厂模式

简单工厂模式不是一个设计模式,这是每个教程里面都会有的一句话,这里也不例外。

简单工厂模式更像是一种编程习惯,其基本理念就是将创建具体类的过程抽取出来,放到某个工厂类的(静态)方法中:

public class SimplePizzaFactory {
  public static Pizza createPizza(String type) {
    Pizza pizza = null;

    if (type.equals("cheese")) {
      pizza = new CheesePizza();
    } else if (type.equals("pepperoni")) {
      pizza = new PepperoniPizza();
    } else if (type.equals("clam")) {
      pizza = new ClamPizza();
    } else if (type.equals("veggie")) {
      pizza = new VeggiePizza();
    }

    return pizza;
  }
}

上面的代码是《Head First 设计模式》一书中的例子,它将创建披萨的过程单独的放在了 createPizza 方法中,使用时只需要传递相应的参数就可以了。

这样一来,在使用简单工厂的地方,我们就只需要依赖一个简单工厂 SimplePizzaFactory 和一个 Pizza 类型,不需要关心具体的 Pizza 类型(依赖倒置原则)。

但是简单工厂模式是存在一些问题的:

  1. 每当我们可以创建的具体对象的类型变多了,我们就需要修改简单工厂的代码,这违反了开闭原则
  2. 假如简单工厂中的代码存在错误,那么,所有使用了这个工厂的地方都会存在安全隐患,这违反了单一职责原则

因此,我们需要改进简单工厂模式,让其遵守面向对象的设计原则。

注:《Head First 设计模式》给出的源码中,方法 createPizza 并不是静态的,理由是:非静态的方法可以通过继承的方式进行重写。但我个人感觉不是很有必要,就改成静态的了。

4 工厂方法模式

工厂方法模式和简单工厂模式有点像,它在父类中声明用于创建具体对象的 抽象方法, 然后由 子类 来实现抽象方法。

通过这种方式我们便可以通过增加子类的方式来扩展工厂的功能,而不是去修改已有的代码,其常见的结构为:

  • 抽象工厂:声明了用于创建具体对象的抽象方法
  • 具体工厂:实现了用于创建具体对象的抽象方法
  • 抽象产品:要被创建的具体对象的抽象父类
  • 具体产品:要被创建的具体对象

类图:

可以看到,工厂方法的扩展方式是通过增加子类实现的,因此避免了修改已有的代码。同时,每个子类只负责创建一种具体对象,符合了单一职责原则。

但有些时候,也不能一味的符合单一职责原则,比如,需要创建一组相关的对象的时候。

5 抽象工厂模式

抽象工厂模式和工厂方法模式很接近,主要用于创建一组相关的对象,也就是经常会在一起被创建的对象,其类图如下:

具体工厂和具体产品之间的依赖关系:

可以发现,抽象工厂模式和工厂方法模式的差别并不是很大,只不过,一个负责创建一组对象,而另一个只创建某一类型的对象。如果将一组对象变成一个对象,那么抽象工厂模式和工厂方法模式就变得差不多了。

虽然在抽象工厂模式中单个具体工厂的职责更多,但是,如果将需要经常在一起创建的对象分散到多个工厂中,反而会增加使用者的依赖和整体的复杂的,因此,抽象工厂模式在创建一组相关的对象的时候还是很有用的。

6 综合理解

设计模式的设计依据是面向对象的设计原则,因此,在理解设计模式的时候,应该参考相应的设计原则。

简单工厂模式虽然简单,但是如果具体对象的类型会经常增加,那么工厂的代码也就会经常修改,频繁的修改就容易出现错误。

而一旦出现错误,由于简单工厂的职责过重,所有使用简单工厂的地方都会出现问题,因此,出现了工厂方法和抽象工厂。

无论是工厂方法还是抽象工厂,添加功能的时候都可以通过增加子类的方式完成,避免了对已有代码的修改。而工厂方法中的具体工厂的职责也很单一,这样一来,出现错误时,受影响的范围可以少一点。

同时,虽然抽象工厂中的具体工厂需要负责创建多个对象,出现错误可能全军覆没,但是,抽象工厂创建的对象本来就是相关的,也就是说,就算分开了,一个地方出错还是会影响整体,因此,将相关的一组对象放在一起创建带来的好处是更多的。

而且,使用这些工厂的时候,使用者都只需要依赖抽象工厂和抽象产品的类型,不需要依赖具体的类型,符合依赖倒置原则。

7 结语

据说学习设计模式容易陷入一个误区,那就是到处都在用设计模式,使得整体代码变得复杂且难以理解。

所以说,应该只在有必要的时候使用设计模式,避免因为设计模式的原因让你的代码太过复杂。

版权声明:本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可