6.2 解 决 方 案

6.2.1 使用工厂方法模式来解决问题

用来解决上述问题的一个合理的解决方案就是工厂方法模式(Factory Method)。那么什么是工厂方法模式呢?

1.工厂方法模式的定义Factory Method

定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类。

2.应用工厂方法模式来解决问题的思路

仔细分析上面的问题,事实上在实现导出数据的业务功能对象里面,根本就不知道究竟要使用哪一种导出文件的格式,因此这个对象根本就不应该和具体的导出文件的对象耦合在一起,它只需要面向导出的文件对象的接口就可以了。

但是这样一来,又有新的问题产生了:接口是不能直接使用的,需要使用具体的接口实现对象的实例。

这不是自相矛盾吗?要求面向接口,不让和具体的实现耦合,但是又需要创建接口的具体实现对象的实例。怎么解决这个矛盾呢?

工厂方法模式的解决思路很有意思,那就是不解决,采取无为而治的方式:不是需要接口对象吗,那就定义一个方法来创建;可是事实上它自己是不知道如何创建这个接口对象的,没有关系,定义成抽象方法就可以了,自己实现不了,那就让子类来实现,这样这个对象本身就可以只是面向接口编程,而无需关心到底如何创建接口对象了。

6.2.2 工厂方法模式的结构和说明

工厂方法模式的结构如图6.3所示。

图片

图6.3 工厂方法模式结构示意图

■ Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。

■ ConcreteProduct:具体的Product接口的实现对象。

■ Creator:创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象。

■ ConcreteCreator:具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例。

6.2.3 工厂方法模式示例代码

(1)Product定义的示例代码如下:

4847ff3fbe2a436eb51132db959134b2

(2)Product实现对象的示例代码如下:

d659f844ea934b0eb39b91a5ebad653f

30c60442d83f4b018dc09ccb48ca6f7c

(3)创建器定义的示例代码如下:

f3ae317018404efbb0abde5cf2169f79

(4)创建器实现对象的示例代码如下:

3a0fb35016154935895139ed4b6f31d9

6.2.4 使用工厂方法模式来实现示例

要使用工厂方法模式来实现示例,先来按照工厂方法模式的结构,对应出哪些是被创建的Product,哪些是Creator。分析要求实现的功能,导出的文件对象接口ExportFileApi就相当于是Product,而用来实现导出数据的业务功能对象就相当于Creator。把Product和Creator分开后,就可以分别来实现它们了。

使用工厂模式来实现示例的程序结构如图6.4所示:

图片

图6.4 使用工厂方法模式来实现示例的程序结构示意图

下面一起来看看代码实现。

(1)导出的文件对象接口ExportFileApi的实现没有变化,这里就不再赘述了。

(2)接口ExportFileApi的实现。为了示例简单,只实现导出文本文件格式和数据库备份文件两种。

实现导出文本文件格式的。示例代码如下:

98b12d27dea24d619101dc9a70645c6d

导出成数据库备份文件形式对象的示例代码如下:

ee09ed9e9169417e87645be159ef52b6

(3)实现ExportOperate的示例代码如下:

0111628e7730414abd10744b82ec6d19

(4)加入了两个Creator实现。

创建导出成文本文件格式对象的示例代码如下:

a535c0b03a6e40828c115aab9002e03e

创建导出成数据库备份文件形式对象的示例代码如下:

602f939ffa2e4361854dbbd2ad5a6369

(5)客户端直接创建需要使用的Creator对象,然后调用相应的功能方法。示例代码如下:

4ece5e48c29846c7bea37f070e85416f

运行结果如下:

导出数据测试数据到数据库备份文件

还可以修改客户端new的对象,切换成其他实现对象,试试看会发生什么。看来应用工厂方法模式是很简单的,对吧。