【设计模式】05 工厂方法模式
5.1 解决方案
5.1.1 使用工厂方法模式来解决问题
- 工厂方法模式的定义
定义一个用于创建对象的接口,让子类决定实例化哪个类, Factory Method 使一个类的实例化延迟到其子类。
- 应用工厂方法解决问题的思路
不解决,采取无为而治的方式
需要接口对象,就定义一个方法来创建,因为这个方法自己也不知道如何创建这个接口,所以把这个方法定义为抽象方法,让子类来实现。
5.1.2 使用工厂方法模式来实现示例
需求:
让文件以不同的形式导出
(1)导出的文件对象的接口
package com.yyh.factory.method;
/**
* 导出文件对象的接口
* @author cyxy
*
*/
public interface ExportFileApi {
/**
* 导出内容成为文件
* @param data 需要保存的数据
* @return 是否保存成功
*/
public boolean export(String data);
}
(2) 导出成文本文件格式
package com.yyh.factory.method;
/**
* 导出成文本文件的格式
* @author cyxy
*
*/
public class ExportTextFile implements ExportFileApi {
public boolean export(String data) {
System.out.println("导出数据 " + data + "到文本文件");
return true;
}
}
(3)导出成数据库备份文件
package com.yyh.factory.method;
/**
* 导出成数据库备份文件形式的对象
* @author cyxy
*
*/
public class ExportDB implements ExportFileApi {
public boolean export(String data) {
System.out.println("导出数据 " + data +" 到数据库备份文件");
return false;
}
}
(4)实现 ExportOperate
package com.yyh.factory.method;
/**
* 实现导出数据的业务功能对象
* @author cyxy
*
*/
public abstract class ExportOperaet {
/**
* 导出文件
* @param data 需要保存的数据
* @return 是否成功导出文件
*/
public boolean export(String data) {
// 使用工厂方法
ExportFileApi api = factoryMethod();
return api.export(data);
}
protected abstract ExportFileApi factoryMethod();
}
(5)两个 Creator 实现
package com.yyh.factory.method;
/**
* 具体的创建器实现对象,实现创建导出成文本文件格式的对象
* @author cyxy
*
*/
public class ExportTextFileOperate extends ExportOperaet {
protected ExportFileApi factoryMethod() {
// 创建导出成文本文件格式的对象
return new ExportTextFile();
}
}
package com.yyh.factory.method;
/**
* 具体的创建器实现对象,实现创建导出成数据库备份文件形式的对象
* @author cyxy
*
*/
public class ExportDBOperate extends ExportOperaet {
// 创建导出成数据库备份文件形式的对象
protected ExportFileApi factoryMethod() {
return new ExportDB();
}
}
(6)客户端
package com.yyh.factory.method;
public class Client {
public static void main(String[] args) {
// 创建需要使用的 Creator 对象
ExportOperaet operate = new ExportDBOperate();
// 调用数据数据的方法
operate.export("测试数据");
}
}
运行结果如下:
导出数据 测试数据 到数据库备份文件
5.2 模式讲解
5.2.1 认识工厂方法模式
- 工厂方法模式的功能
工厂方法模式的主要功能是让父类在不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现。
2. 完成抽象类
在工厂方法模式的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法。
3. 完成具体的类
具体的子类来决定具体要如何创建父类所需的对象。
4. 工厂方法的参数和返回
在抽象方法中传递参数,在子类实现的时候根据参数进行选择。
5. 谁来使用工厂方法创建的对象
在 Create 中的其他方法使用工厂方法创建的对象。
5.2.2 工厂方法模式与 IoC/DI
IoC —— Inversion of Control,控制反转
DI —— Dependency Injection,依赖注入
- 如何理解 IoC/DI
(1)参与者都有谁:一般有三个参与者,一个是某个对象;另一个是 IoC/DI 的容器,还有一个是某个对象的外部资源。
(2)谁依赖于谁:当然是某个对象依赖于 IoC/DI 的容器。
(3)为什么需要依赖:对象需要 IoC/DI 的容器来提供对象需要的外部资源。
(4)谁注入于谁:很明显是 IoC/DI 的容器注入某个对象
(5)到底注入什么:就是注入某个对象所需的外部资源。
(6)谁控制谁:当然是 IoC/DI 的容器控制对象。
(7)控制什么:主要是控制对象实例的创建。
(8)为何叫反转:反转是相对于正向而言的,A 类不再主动去获取 C,而是被动等待,等待 IoC/DI 的容器获取一个 C 的实例,然后反向地注入到 A 类中。
(9)依赖注入和控制反转是同一概念吗?
- 依赖注入:应用程序依赖容器创建并注入它所需要的外部资源
- 控制反转:容器控制应用程序,由容器反向地向应用程序注入其所需要的外部资源
- 工厂方法模式和 IoC/DI 的关系
package com.yyh.factory.method;
public abstract class A {
/**
* 工厂方法,创建 C1,类似于从子类注入进来的途径
* @return C 的对象实例
*/
protected abstract C createC();
public void t1() {
// 这里需要使用 C 类, 可是不知道究竟是用哪一个
// 也就不主动去创建 C 了
// 反正会在子类里面实现,这里不用管怎么获取 C,直接使用就好了
createC().tc();
}
}
package com.yyh.factory.method;
public class A2 extends A {
protected C createC() {
// 真正的选择具体实现,并创建对象
return new C2();
}
}
package com.yyh.factory.method;
public interface C {
public void tc();
}
package com.yyh.factory.method;
public class C2 implements C {
public void tc() {
System.out.println("测试");
}
}
5.3.3 参数化工厂方法
通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象
package com.yyh.factory.method;
/**
* 实现导出数据的业务员功能对象
* @author cyxy
*
*/
public class ExportOperate {
/**
* 导出文件
* @param type 用户选择导出的类型
* @param data 需要保存的数据
* @return 是否成功导出文件
*/
public boolean export(int type, String data) {
// 使用工厂方法
ExportFileApi api = factoryMethod(type);
return api.export(data);
}
protected ExportFileApi factoryMethod(int type) {
ExportFileApi api = null;
// 根据类型来选择究竟要创建哪一种导出文件对象
if (type == 1) {
api = new ExportTextFile();
} else if (type == 2) {
api = new ExportDB();
}
return api;
}
}
package com.yyh.factory.method;
public class Client {
public static void main(String[] args) {
// 创建需要使用的 Creator 对象
ExportOperate operate = new ExportOperate();
// 调用输出数据的功能方法,传入选择导出类型的参数
operate.export(1, "测试数据");
}
}
使用参数化工厂方法,扩展起来会非常容易。
package com.yyh.factory.method;
/**
* 扩展 ExportOperate 对象,加入可以导出的 XML 文件
* @author cyxy
*
*/
public class ExportOperate2 extends ExportOperate {
protected ExportFileApi factoryMethod(int type) {
ExportFileApi api = null;
// 可以全部覆盖,也可以选择自己感兴趣的覆盖
// 这里只想 添加自己新的实现,其他的不管
if (type == 3) {
api = new ExportXml();
} else {
// 其他的还是让父类来实现
api = super.factoryMethod(type);
}
return api;
}
}
5.3.4 工厂方法模式的优缺点
工厂方法模式的优点:
- 可以在不知道实现的情况下编程
- 更容易扩展对象的新版本
工厂方法模式的缺点:
- 具体产品对象和工厂方法的耦合性。
5.3.5 思考工厂方法模式
- 工厂方法模式的实质
工厂方法模式的本质:延迟到子类来选择实现
- 对设计原则的体现
工厂方法模式很好的体现了"依赖倒置原则"。
依赖倒置原则告诉我们 “要依赖抽象,不要依赖与具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。
- 何时选用工厂方法模式
- 如果一个类需要创建某个接口的对象,但又不知道具体的实现类。
- 如果一个类本身就希望由它的子类来创建所需的对象的时候。
上一篇: Java实现工厂方法模式,以及对工厂方法模式的见解
下一篇: 设计模式——工厂方法模式