欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【设计模式】05 工厂方法模式

程序员文章站 2022-03-09 16:32:55
...

5.1 解决方案

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

  1. 工厂方法模式的定义

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

  1. 应用工厂方法解决问题的思路

不解决,采取无为而治的方式

需要接口对象,就定义一个方法来创建,因为这个方法自己也不知道如何创建这个接口,所以把这个方法定义为抽象方法,让子类来实现。

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 认识工厂方法模式

  1. 工厂方法模式的功能

工厂方法模式的主要功能是让父类在不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现。

2. 完成抽象类

在工厂方法模式的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法。

3. 完成具体的类

具体的子类来决定具体要如何创建父类所需的对象。

4. 工厂方法的参数和返回

在抽象方法中传递参数,在子类实现的时候根据参数进行选择。

5. 谁来使用工厂方法创建的对象

在 Create 中的其他方法使用工厂方法创建的对象。

5.2.2 工厂方法模式与 IoC/DI

IoC —— Inversion of Control,控制反转

DI —— Dependency Injection,依赖注入

  1. 如何理解 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)依赖注入和控制反转是同一概念吗?

  • 依赖注入:应用程序依赖容器创建并注入它所需要的外部资源
  • 控制反转:容器控制应用程序,由容器反向地向应用程序注入其所需要的外部资源
  1. 工厂方法模式和 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 思考工厂方法模式

  1. 工厂方法模式的实质

工厂方法模式的本质:延迟到子类来选择实现

  1. 对设计原则的体现

工厂方法模式很好的体现了"依赖倒置原则"。

依赖倒置原则告诉我们 “要依赖抽象,不要依赖与具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。

  1. 何时选用工厂方法模式
  • 如果一个类需要创建某个接口的对象,但又不知道具体的实现类。
  • 如果一个类本身就希望由它的子类来创建所需的对象的时候。
相关标签: 工厂方法模式