9.3.3 工厂方法和抽象工厂

9.3.3 工厂方法和抽象工厂

在简单工厂模式里,系统使用工厂类生产所有产品实例,且该工厂类决定生产哪个类的实例,即该工厂类负责所有的逻辑判断、实例创建等工作。
如果不想在工厂类中进行逻辑判断,程序可以为不同产品类提供不同的工厂,不同的工厂类生产不同的产品。例如为上面的PrinterBetter Printer分别提供PrinterFactoryBetterPrinterFactory工厂类,这就无须在工厂类进行复杂的逻辑判断。

程序示例

1
2
3
4
5
6
7
8
9
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\FactoryMethod
└─src\
├─BetterPrinter.java
├─BetterPrinterFactory.java
├─Computer.java
├─Output.java
├─OutputFactory.java
├─Printer.java
└─PrinterFactory.java

本示例应用将OutputFactory改为一个接口,并为该接口提供两个实现类:PrinterFactory.javaBetterPrinterFactory.java。下面是OutputFactory接口的代码。

1
2
3
4
5
public interface OutputFactory
{
// 仅定义一个方法用于返回输出设备。
Output getOutput();
}

上面的OutputFactory只是一个接口,该接口提供了一个getOutput0方法,该方法可直接返回一个输出设备。
下面为OutputFactory接口提供一个PrinterFactory实现类,该实现类专门负责生成Printer实例。

1
2
3
4
5
6
7
8
public class PrinterFactory implements OutputFactory
{
public Output getOutput()
{
// 该工厂只负责产生Printer对象
return new Printer();
}
}

上面的PrinterFactory实现了OutputFactory接口,并实现了该接口里的getOutput()方法,该方法直接返回一个简单的Printer对象。
下面再为OutputFactory接口提供一个BetterPrinterFactory实现类,该实现类专门负责生成BetterPrinter实例。

1
2
3
4
5
6
7
8
public class BetterPrinterFactory implements OutputFactory
{
public Output getOutput()
{
// 该工厂只负责产生BetterPrinter对象
return new BetterPrinter();
}
}

本示例应用中各类之间的类图如图9.2所示。
这里有一张图片
当使用工厂方法设计模式时,对象调用者需要与具体的工厂类耦合:当需要不同对象时,程序需要调用相应工厂对象的方法来得到所需的对象。如下是Computer类中创建Output对象并调用该对象方法的代码。

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
public class Computer
{
private Output out;

public Computer(Output out)
{
this.out = out;
}
// 定义一个模拟获取字符串输入的方法
public void keyIn(String msg)
{
out.getData(msg);
}
// 定义一个模拟打印的方法
public void print()
{
out.out();
}
public static void main(String[] args)
{
// 使用PrinterFactory子类来创建OutputFactory
OutputFactory of = new BetterPrinterFactory();
// 将Output对象传入,创建Computer对象
Computer c = new Computer(of.getOutput());
c.keyIn("轻量级Java EE企业应用实战");
c.keyIn("疯狂Java讲义");
c.print();
}
}

正如程序中main方法中第一行代码所示,当客户端代码需要调用Ouput对象的方法时,为了得到不同的Output实例,程序必须显式创建不同的OutputFactory实例,程序中创建的是PrinterFactory实例。
从上面的代码可以看出,对于采用工厂方法的设计架构,客户端代码成功与被调用对象的实现类分离,但带来了另一种耦合:客户端代码与不同的工厂类耦合。这依然是一个问题!

抽象工厂模式

为了解决客户端代码与不同工厂类耦合的问题,接着考虑再增加一个工厂类,该工厂类不是生产Output对象,而是生产OutputFactory实例,简而言之,这个工厂类不制造具体的被调用对象,而是制造不同工厂对象。这个特殊的工厂类被称呼抽象工厂类,这种设计方式也被称为抽象工厂模式。如图9.3所示是抽象工厂模式示例的UML类图。
这里有一张图片
从图9.3中可以看出,在这种模式下系统新增了一个OutputFactoryFactory工厂类,该工厂类提供了一个getOutputFactory(String type)方法,该方法用于返回一个OutputFactory工厂实例。

程序示例

1
2
3
4
5
6
7
8
9
10
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\AbstractFactory
└─src\
├─BetterPrinter.java
├─BetterPrinterFactory.java
├─Computer.java
├─Output.java
├─OutputFactory.java
├─OutputFactoryFactory.java
├─Printer.java
└─PrinterFactory.java

下面是该抽象工厂类的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class OutputFactoryFactory
{
// 仅定义一个方法用于返回输出设备。
public static OutputFactory getOutputFactory(String type)
{
if (type.equalsIgnoreCase("better"))
{
return new BetterPrinterFactory();
} else
{
return new PrinterFactory();
}
}
}

从上面的粗体字代码可以看出,抽象工厂根据type参数进行判断,决定需要生成哪种工厂实例。通过这种设计模式,就可让客户端程序只需与抽象工厂类耦合。下面是客户端调用被调用者对象方法的主方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Computer
{
...
public static void main(String[] args)
{
// 使用OutputFactoryFactory工厂类创建OutputFactory
OutputFactory of = OutputFactoryFactory.getOutputFactory("better");
// 调用OuputFactory的方法获取Output对象,
// 并将Output对象传入,创建Computer对象
Computer c = new Computer(of.getOutput());
c.keyIn("轻量级Java EE企业应用实战");
c.keyIn("疯狂Java讲义");
c.print();
}
}

上面程序中的粗体字代码用于产生一个OutputFactory工厂,但具体产生哪个工厂则由OutputFactoryFactory抽象工厂决定,不同的工厂对象将产生不同的Output对象。

通过采用抽象工厂的设计模式,系统可以让客户端代码与被调用对象的实现类、具体的工厂类分离。

读者掌握了这种抽象工厂模式后,应该对Spring IoC容器感到迷惑:它到底是简单工厂,还是抽象工厂?本书倾向于认为Spring loc容器是抽象工厂,因为Sping loc容器可以包括万象,它不仅可以管理普通Bean实例,也可管理工厂实例。
不要过分纠缠于简单工厂模式、抽象工厂模式这些概念,可以把它们统称为工厂模式

  • 如果工厂直接生产被调用对象,那就是简单工厂模式;
  • 如果工厂生产了工厂对象,那就会升级成抽象工厂模式。