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

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. 何时选用工厂方法模式
  • 如果一个类需要创建某个接口的对象,但又不知道具体的实现类。
  • 如果一个类本身就希望由它的子类来创建所需的对象的时候。

猜你喜欢

转载自blog.csdn.net/qq_37581282/article/details/83006547