创建型——工厂模式

本文介绍一下设计模式中的工厂模式。工厂,在现实生活中是用来生产产品的,在OO编程中,对象就是产品,也就是用来生产对象的。和工厂相关的设计模式主要有三种:简单工厂模式、工厂方法模式和抽象工厂模式。下面,我们分别叫啥下这三种模式。

简单工厂模式

什么是简单工厂模式?

所谓“简单工厂”,就是把实例化操作单独放到一个类中(简单工厂类),让该类来决定应该用哪个具体子类来实例化。

为什么要用简单工厂模式?

在日常开发中,有些时候需要生成复杂的对象,用简单工厂模式可以达到“创建与使用分离”的目的。客户往往为了系统更具有弹性,可能希望new一个上层抽象类或接口,而不关注具体是什么子(实现)类。举个例子,Pizza是个抽象类,CheesePizzaGreekPizza是具体实现类,客户请求披萨的代码可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
Pizza orderPizza(String type){
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}
pizza.prepare();
pizza.bake();
...
}

此时,如果再加入更多类型的pizza,new一个对象的逻辑就变得更复杂了,而且需要更改所有客户的代码。

怎么用简单工厂模式?

还是上面Pizza那个例子,我们可以创建一个简单工厂类,将实例化Pizza的工作交给工厂来做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}
return pizza;
}
}
//客户中orderPizza方法
Pizza orderPizza(String type){
//客户持有SimplePizzaFactory实例
Pizza pizza = factory.createPizza(type);
pizza.prepare();
...
}

可以看到,原来的系统中,每个客户都要清楚了解Pizza所有子类,并和这些子类耦合在一起。但是引入简单工厂后,客户只需要耦合Pizza工厂类即可,不需要知道有多少类型pizza,系统的耦合性大大降低。

简单工厂模式的类图是啥样?

优缺点总结

  • 优点:很简单,最终目的就是“使用和创建分离”,降低了系统耦合度,增加了系统可扩展性。
  • 缺点
    • 工厂类单一,职责过重,代码可能过于臃肿;
    • 扩展困难,增加新类型,需要修改工厂类代码,不满足“开闭原则”;
    • 工厂类创建对象的方法一般是static,难以继承扩展。

工厂方法模式

有了简单工厂,为什么要有工厂方法?

工厂方法模式,是在简单工厂的基础上进行再抽象。

简单工厂具有以下缺点:

  1. 当产品类型过多时,逻辑过于复杂,容易出错;
  2. 所有类型产品的创建都放在一个工厂类中,过于臃肿、职责过重;
  3. 简单工厂,同场使用static方法,不能继承;
  4. 不满足“开闭原则”,当新加入一些类型时,需要修改工厂类。

工厂方法模式长啥样?

为了解决这些问题,在工厂方法模式中,不再由工厂类创建对象,而是将该任务转嫁给工厂类的各个子类来完成,由子类来决定该实例化哪一个类。工厂方法模式的类图如下:

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//创建抽象工厂
abstract class Factory{
public abstract Product Manufacture();
}
//创建抽象产品类
abstract class Product{
public abstract void Show();
}
//具体产品A类
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生产出了产品A");
}
}

//具体产品B类
class ProductB extends Product{

@Override
public void Show() {
System.out.println("生产出了产品B");
}
}
//工厂A类 - 生产A类产品
class FactoryA extends Factory{
@Override
public Product Manufacture() {
return new ProductA();
}
}

//工厂B类 - 生产B类产品
class FactoryB extends Factory{
@Override
public Product Manufacture() {
return new ProductB();
}
}
//生产工作流程
public class FactoryPattern {
public static void main(String[] args){
//客户要产品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.Manufacture().Show();

//客户要产品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.Manufacture().Show();
}
}

优缺点总结

  • 优点:基本对应我们前文提到的简单工厂存下的缺点。
    • 符合开闭原则、单一职责原则、不适用静态方法,可以继承等等。
  • 缺点
    • 类变多了,系统更加复杂,实现时可能还要用DOM、反射等技术;
    • 一个具体工厂只能创建一种具体产品。

抽象工厂模式

有了工厂方法,为什么还要有抽象工厂?

前面提到,工厂方法模式的一个缺点是:一个具体工厂只能创建一种具体产品。但是在实际应用中,一个产品可能由多个零部件构成,此时需要将多个子产品组合成一个完整产品。

比如,我们要组装一台电脑,可能用到主板和CPU等各种零部件,为了方便起见这里只考虑CPU和主板。

一种解决方案是,主板和CPU分别按照工厂方法模式设计,这样有两个抽象工厂方法类,用户可以任意组合具体类型CPU和主板。但是如果考虑CPU和主板之间的兼容性,这种设计方案就无法满足需求了,需要用到抽象工厂模式重新设计。

什么是抽象工厂?

抽象工厂的本质是:将多个抽象工厂方法封装到一起,用于生产一系列相关对象(核心功能),这些对象组合在一起是一个更大的产品。这是它和工厂方法模式最大的不同。抽象工厂模式的类图如下:

总结

三种工厂模式对比

  • 简单工厂,与其说是一种设计模式,不如说是一种编码习惯(因为它太简单了),其核心操作就是:把创建对象的复杂逻辑单独封装成一个类,降低系统耦合性,达到“使用与创建”分离的目的。
  • 工厂方法,是在简单工厂基础上的进一步抽象,将创建对象的操作转嫁给抽象工厂的具体实现类。这样每种具体工厂类负责生产一种类型产品,系统可扩展性进一步提高,符合“开闭原则”。
  • 抽象工厂,工厂方法适合生产单一产品,可以理解成这个产品就是最小单元,不能拆分成更小的产品。但是往往一个产品是由若干个子产品组合而成,此时就需要抽象工厂上场了。抽象工厂的核心就是把若干个抽象工厂方法封装到一起。

JDK中的应用举例

  • java.util.Calendar类中getInstance方法就使用了简单工厂。
  • java.util.Collection接口的iterator方法运用了抽象工厂,具体生成什么类型的迭代器由子类决定。

Spring中应用举例

  • Spring中的BeanFactory运用了抽象工厂,其子接口子接口 ConfigurableBeanFactory 接口运用了工厂方法。

参考资料

  1. https://www.jianshu.com/p/d0c444275827
  2. https://pdai.tech/md/dev-spec/pattern/5_abstract_factory.html
  3. https://pdai.tech/md/dev-spec/pattern/4_factory_method.html
  4. https://pdai.tech/md/dev-spec/pattern/3_simple_factory.html
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信