设计模式之工厂模式
Table of Contents
前言
在多次接触设计模式的过程中,工厂模式留给我的印象是最深的,因为,它基本上是各种教程前面必然会出现的一个模式 (¬_¬)
所以,我觉得还是有必要总结一下和工厂模式有关的内容。
三个面向对象设计原则
某种程度上,设计模式的设计依据便是面向对象的设计原则,这里先列出来个人认为有助于工厂模式的理解的三个原则:
- 开闭原则:类应该对扩展开发,对修改关闭。也就是说,我们应该在尽量避免修改已有代码的情况下扩展增加功能
- 依赖倒置原则:依赖抽象,不要依赖具体类。也就是说,我们应该避免对具体类的依赖,而要尽量的依赖抽象
- 单一职责原则:类的职责要单一,不要将太多的职责放在一个类中
简单工厂模式
简单工厂模式不是一个设计模式,这是每个教程里面都会有的一句话,这里也不例外。
简单工厂模式更像是一种编程习惯,其基本理念就是将创建具体类的过程抽取出来,放到某个工厂类的(静态)方法中:
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
类型(依赖倒置原则)。
但是简单工厂模式是存在一些问题的:
- 每当我们可以创建的具体对象的类型变多了,我们就需要修改简单工厂的代码,这违反了开闭原则
- 假如简单工厂中的代码存在错误,那么,所有使用了这个工厂的地方都会存在安全隐患,这违反了单一职责原则
因此,我们需要改进简单工厂模式,让其遵守面向对象的设计原则。
注:《Head First 设计模式》给出的源码中,方法 createPizza
并不是静态的,理由是:非静态的方法可以通过继承的方式进行重写。但我个人感觉不是很有必要,就改成静态的了。
工厂方法模式
工厂方法模式和简单工厂模式有点像,它在父类中声明用于创建具体对象的 抽象方法, 然后由 子类 来实现抽象方法。
通过这种方式我们便可以通过增加子类的方式来扩展工厂的功能,而不是去修改已有的代码,其常见的结构为:
- 抽象工厂:声明了用于创建具体对象的抽象方法
- 具体工厂:实现了用于创建具体对象的抽象方法
- 抽象产品:要被创建的具体对象的抽象父类
- 具体产品:要被创建的具体对象
类图:
可以看到,工厂方法的扩展方式是通过增加子类实现的,因此避免了修改已有的代码。同时,每个子类只负责创建一种具体对象,符合了单一职责原则。
但有些时候,也不能一味的符合单一职责原则,比如,需要创建一组相关的对象的时候。
抽象工厂模式
抽象工厂模式和工厂方法模式很接近,主要用于创建一组相关的对象,也就是经常会在一起被创建的对象,其类图如下:
具体工厂和具体产品之间的依赖关系:
可以发现,抽象工厂模式和工厂方法模式的差别并不是很大,只不过,一个负责创建一组对象,而另一个只创建某一类型的对象。如果将一组对象变成一个对象,那么抽象工厂模式和工厂方法模式就变得差不多了。
虽然在抽象工厂模式中单个具体工厂的职责更多,但是,如果将需要经常在一起创建的对象分散到多个工厂中,反而会增加使用者的依赖和整体的复杂的,因此,抽象工厂模式在创建一组相关的对象的时候还是很有用的。
综合理解
设计模式的设计依据是面向对象的设计原则,因此,在理解设计模式的时候,应该参考相应的设计原则。
简单工厂模式虽然简单,但是如果具体对象的类型会经常增加,那么工厂的代码也就会经常修改,频繁的修改就容易出现错误。
而一旦出现错误,由于简单工厂的职责过重,所有使用简单工厂的地方都会出现问题,因此,出现了工厂方法和抽象工厂。
无论是工厂方法还是抽象工厂,添加功能的时候都可以通过增加子类的方式完成,避免了对已有代码的修改。而工厂方法中的具体工厂的职责也很单一,这样一来,出现错误时,受影响的范围可以少一点。
同时,虽然抽象工厂中的具体工厂需要负责创建多个对象,出现错误可能全军覆没,但是,抽象工厂创建的对象本来就是相关的,也就是说,就算分开了,一个地方出错还是会影响整体,因此,将相关的一组对象放在一起创建带来的好处是更多的。
而且,使用这些工厂的时候,使用者都只需要依赖抽象工厂和抽象产品的类型,不需要依赖具体的类型,符合依赖倒置原则。
结语
据说学习设计模式容易陷入一个误区,那就是到处都在用设计模式,使得整体代码变得复杂且难以理解。
所以说,应该只在有必要的时候使用设计模式,避免因为设计模式的原因让你的代码太过复杂。