工厂方法模式(Java)

一:工厂方法模式的定义

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

二:工厂方法模式的结构和说明图


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

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

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

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

步骤一:创建Product接口,定义Product接口的抽象方法;

步骤二:实现Product接口创建类ConcreteProduct,实现Product接口的抽象方法;

步骤三:创建抽象工厂类Creator,定义抽象工厂类的抽象方法;

步骤四:继承抽象工厂类Creator创建类ConcreteCreator,实现抽象工厂类的抽象方法。

步骤五:客户Client通过调用具体工厂类ConcreteCreator的方法,从而创建不同具体产品类的实例。

三:工厂方法模式的示例代码

/**
 * 工厂方法所创建的对象的接口
 * @author Peter
 */
public interface Product {

	//可以定义Product的属性和方法
	
}
/**
 * 具体的Product对象
 * @author Peter
 */
public class ConcreteProduct implements Product {

	//实现Product要求的方法
}
/**
 * 创建器,声明工厂方法
 * @author Peter
 */
public abstract class Creator {

	 /**
	  * 创建Product的工厂方法
	  * @return
	  */
	protected abstract Product factoryMethod();
	
	/**
	 * 示意方法,实现某些功能的方法
	 */
	public void someOperation(){
		//通常在这些方法实现中需要调用工厂方法来获取Product对象
		Product product = factoryMethod();
	}
}
/**
 * 具体的创建器实现对象
 * @author Peter
 */
public class ConcreteCreator extends Creator {

	@Override
	protected Product factoryMethod() {
		//重定义工厂方法,返回一个具体的Product对象
		return null;
	}
}

四:用工厂模式实现一个实际的例子

一个系统导出数据,要么导出在DB中做备份,要么导出在File中做备份。

1:创建导出的文件对象的接口,定义导出内容成为文件的抽象方法

/**
 * 创建导出的文件对象的接口
 * @author Peter
 */
public interface ExportFileApi { //相当于Product

	/**
	 * 导出内容成为文件
	 * @param data
	 * @return
	 */
	public boolean export(String data);
}

2:创建导出的文件对象的接口的实现类,有两种方式实现导出数据:DB;File

/**
 * 导出成数据库备份文件形式的对象
 * @author Peter
 */
public class ExportDB implements ExportFileApi { //相当于ConcreteProduct

	//导出成DB备份的实现
	public boolean export(String data) {
		//简单示意一下,这里需要操作数据库和文件
		System.out.println("导出数据" + data + "到数据库备份文件");
		return true;
	}
}
/**
 * 导出成文本文件格式的对象
 * @author Peter
 */
public class ExportTxtFile implements ExportFileApi { //相当于ConcreteProduct

	//导出成File备份的实现
	public boolean export(String data) {
		//简单示意一下,这里需要操作文件
		System.out.println("导出数据" + data + "到文本文件.");
		return true;
	}
}

3:创建导出数据的抽象工厂类,定义抽象工厂类的抽象方法;

/**
 * 实现导出数据的业务功能对象
 * @author Peter
 */
public abstract class ExportOperate { //相当于Creator

	/**
	 * 导出文件,提供给外部使用的方法
	 * @param data
	 * @return
	 */
	public boolean export(String data){
		//使用工厂方法
		ExportFileApi api = factoryMethod();
		return api.export(data);
	}
	
	/**
	 * 工厂方法,创建导出的文件对象的接口对象,一般不对外
	 * @return
	 */
	protected abstract ExportFileApi factoryMethod();
}

4:创建导出数据长度抽象工厂的实现类,实现抽象工厂类的抽象方法,具体导出到DB还是File。

/**
 * 具体的创建器实现对象,实现创建导出数据库备份文件形式的对象
 * @author Peter
 */
public class ExportDBOperate extends ExportOperate { //相当于ConcreteCreator

	@Override
	protected ExportFileApi factoryMethod() {
		//创建导出成数据库备份文件形式的对象
		return new ExportDB();
	}
}
/**
 * 具体的创建器实现对象,实现创建导出成文本文件格式的对象
 * @author Peter
 */
public class ExportTxtFileOperate extends ExportOperate { //相当于ConcreteCreator

	@Override
	protected ExportFileApi factoryMethod() {
		//创建导出成文本文件格式的对象
		return new ExportTxtFile();
	}
}

5:Client通过调用具体工厂类的方法,具体实现导出Db还是File的实例。

public class Client { //客户端

	public static void main(String[] args) {
		//创建使用的creator对象
		ExportOperate operate = new ExportDBOperate();
		//调用输出数据的功能方法
		operate.factoryMethod().export("测试数据");
	}
}

五:工厂方法模式讲解

1:工厂方法模式的功能

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

2:实现成抽象类

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

注:子类在实现这些抽象方法的时候,通常并不是真正地由子类来实现具体的功能,而是在子类方法里面做选择,选择具体的产品实现对象。

父类里面,通常会有使用这些产品对象来实现一定的功能的方法,而且这些方法所实现的功能通常都是公共的功能,不管子类选择了何种具体的产品实现,这些方法的功能总是能正确执行。

3:实现成具体的类

可以把父类实现成为一个具体的类。这种情况下,通常是在父类中提供获取所需对象的默认实现方法,这样即使没有具体的子类,也能够运行。

通常这种情况还是需要具体的子类来决定具体要如何创建父类所需要的队形。也把这种情况称为工厂方法为子类提供了挂钩。通过工厂方法,可以让子类对象来覆盖父类的实现,从而提供更好的灵活性。

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

工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现。也就是说通过在抽象方法里面传递参数,在子类实现的时候根据参数进行选择,看看究竟应该创建哪一个具体的实现对象。

一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体的类的实例。

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

在工厂方法模式里面,应该是Creator中的其他方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给Creator外部使用,但工厂方法模式的本意是由Creator对象内部的方法来使用工厂方法创建的对象,即工厂方法一般不提供给Creator外部使用。

客户端应该使用Creator对象,或者是使用由Creator创建出来的对象。对于客户端使用Creator对象,这个时候工厂方法创建的对象,是Creator中的某些方法使用;对于使用那些由Creator创建出来的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。

(1)客户端使用Creator的情况

比如前面的示例,对于“实现导出数据的业务功能对象”的类ExportOperate,它有一个export的方法,在这个方法里面,需要使用具体的“导出的文件对象的接口对象”ExportFileApi,而ExportOperate是不知道具体的ExportFileApi实现的,它是定义了一个工程方法,用来返回ExportFileApi的对象,然后export方法会使用这个工厂方法来获取它所需要的对象,然后执行功能。

这个时候的客户端主要就是使用ExportOperate的实例来完成它想要完成的功能,即客户端使用Creator对象的情况。代码如下:

/**
 * 客户端使用Creator对象的情况下,Creator的基本实现结构
 * @author Peter
 */
public abstract class Creator {
	
	/**
	 * 工厂方法一般不对外
	 * @return
	 */
	protected abstract Product factoryMethod();
	
	/**
	 * 提供给外部使用的方法
	 * 客户端一般使用Creator提供的这些方法来完成所需要的功能。
	 */
	public void someOperation(){
		//这里使用工厂方法
		Product p = factoryMethod();
	}
}

(2)客户端使用由Creator创建出来的对象

由Creator向客户端返回由“工厂方法创建的对象”来构建的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。代码如下:

/**
 * 客户端使用Creator来创建客户端需要的对象的情况下,Creator的基本实现结构
 * @author Peter
 */
public abstract class Creator {
	/**
	 * 工厂方法,一般不对外,创建一个部件对象
	 * @return 创建的产品对象,一般是另一个产品对象的部件
	 */
	protected abstract Product1 factoryMethod1();
	
	/**
	 * 工厂方法,一般不对外,创建一个部件对象
	 * @return 创建的产品对象,一般是另一个产品对象的部件
	 */
	protected abstract Product2 factoryMethod2();

	/**
	 * 创建客户端需要的对象,客户端主要使用产品对象来完成所需要的功能
	 * @return 客户端需要的对象
	 */
	public Product createProduct(){
		//在这里使用工厂方法,得到客户端所需对象的部件对象
		Product1 p1 = factoryMethod1();
		Product2 p2 = factoryMethod2();
		
		//工厂方法创建的对象是创建客户端对象所需要的
		Product p = new CreateProduct();
		p.setProduct1(p1);
		p.setProduct2(p2);
		return p;
	}
}

注:在工厂方法模式里面,客户端要么使用Creator对象,要么使用Creator创建的对象,一般客户端不直接使用工厂方法。当然也可以直接把工厂方法暴露给客户端操作,但是一般不这么做。

6:工厂方法模式的调用顺序示意图


客户端使用由Creator创建出来的对象


客户端使用Creator对象

六:参数化工厂方法

1:代码实现

/**
 * 导出的文件对象接口
 * @author Peter
 */
public interface ExportFileApi {
	
	/**
	 * 导出内容成为文件
	 * @param data
	 * @return
	 */
	public boolean export(String data);
}
/**
 * 导出成数据库备份文件形式的对象
 * @author Peter
 */
public class ExportDB implements ExportFileApi {

	@Override
	public boolean export(String data) {
		System.out.println("导出数据" + data + "到数据库备份文件");
		return true;
	}
}
/**
 * 导出成文本文件格式的对象
 * @author Peter
 */
public class ExportTxtFile implements ExportFileApi {

	@Override
	public boolean export(String data) {
		System.out.println("导出数据" + data + "到文本文件!");
		return true;
	}
}
/**
 * 实现导出数据的业务功能对象
 * @author Peter
 */
public class ExportOperate {

	/**
	 * 工厂方法,创建导出的文件对象的接口对象
	 * @param type
	 * @return
	 */
	protected ExportFileApi factoryMethod(int type){
		ExportFileApi api = null;
		//根据类型选择究竟要创建哪一种导出文件对象
		if(type == 1){
			api = new ExportDB();
		}else if(type == 2){
			api = new ExportTxtFile();
		}
		return api;
	}
	
	/**
	 * 导出文件
	 * @param type
	 * @param data
	 * @return
	 */
	public boolean export(int type, String data){
		//使用工厂方法
		ExportFileApi api = factoryMethod(type);
		return api.export(data);
	}
}
public class Client {

	public static void main(String[] args) {
		//创建需要使用的creator对象
		ExportOperate operate = new ExportOperate();
		//调用输出数据的功能方法,传入选择导出类型的参数
		operate.export(2, "测试数据");
	}
}
注:上面是一种很常见的参数化工厂方法的实现方式,但是也还是有把参数化工厂方法实现成为抽象的,这点要注意,并不是说参数化工厂方法就不能实现成为抽象类了。只是一般情况下,参数化工厂方法,在父类都会提供默认的实现。
2:扩展参数化工厂方法,扩展出导出成xml文件

public class ExportXml implements ExportFileApi { //xml导出

	@Override
	public boolean export(String data) {
		System.out.println("导出数据" + data + "到xml文件");
		return true;
	}
}
public class ExportOperate1 extends ExportOperate { //扩展ExportOperate对象,加入导出xml功能

	protected ExportFileApi factoryMethod(int type){
		ExportFileApi api = null;
		if(type == 3){
			api = new ExportXml();
		}else{
			api = super.factoryMethod(type); //其他还是选择父类实现
		}
		return api;
	}
}
public class Client {
	public static void main(String[] args) {
		//创建需要使用的creator对象
		ExportOperate operate = new ExportOperate1();
		//调用输出数据的功能方法,传入选择导出类型的参数
		operate.export(1, "测试数据");
		operate.export(2, "测试数据");
		operate.export(3, "测试数据");
	}
}

七:工厂方法模式的优缺点

1:工厂方法模式的优点

(1):可以在不知具体实现的情况下编程(工厂方法模式可以让你在实现功能的时候,如果需要某个产品对象,只需要使用产品的接口即可,而无须关心具体的实现。选择具体实现的任务延迟到子类去完成。);

(2):共容易扩展对象的新版本(工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易,比如上面的示例参数化工厂方法);

(3):连接平行的类层次;

2:工厂方法模式的缺点

(1):具体产品对象和工厂方法的耦合性(在工厂方法模式中,工厂方法是需要创建产品对象的,也就是需要选择具体的产品对象,并创建它们的实例,因此具体产品对象和工厂方法是耦合的)

八:小思工厂方法模式

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

2:简单工厂和工厂方法区别

从本质上讲,它俩确实是非常类似的,在具体实现上都是“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里面进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。(其实如果把工厂模式中的Creator退化一下,只提供工厂方法,而且这些工厂方法还都提供默认的实现,那就变成了简单工厂,比如上面的参数化工厂方法中去掉export方法)

3:工厂方法模式很好地体现了“依赖倒置原则

依赖倒置原则告诉我们“要依赖抽象,不要依赖具体类”,简单点说就是:不能让高层组件依赖于底层组件,而且不管高层组件还是底层组件,都应该依赖于抽象。倒置的是这个接口的“所有权”。

4:何时选用工厂方法

(1):如果一个类要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现;

(2):如果一个类本身就希望由它的子类来创建所需的对象的时候,应该使用工厂方法模式。





猜你喜欢

转载自blog.csdn.net/u013132035/article/details/80715030