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

类型:对象创建型模式

类图:


  • Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。
  • ConcreteProduct:具体Product接口的实现对象。
  • Creator:创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator中提供默认实现,让工厂方法返回一个缺省的Product类型的实例对象。
  • ConcreteCreator:具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例对象

工厂方法模式代码

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

/**
 * 工厂方法所需要创建的对象的接口
 * @author FX_SKY
 *
 */
public interface Product {
	//可以定义Product的属性和方法,这里示例一下
	public void execute();
}



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

/**
 * 具体的Product对象
 * @author FX_SKY
 *
 */
public class ConcreteProduct implements Product {

	@Override
	public void execute() {
		// TODO Auto-generated method stub
	}

}



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

/**
 * 创建器,声明工厂方法
 * @author FX_SKY
 *
 */
public abstract class Creator {

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



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

public class ConcreteCreator extends Creator {

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

}

场景应用-- 导出数据的应用框架

考虑这样的一个实际应用:实现一个导出数据的应用框架,来让客户选择数据的导出方法,并真正执行文件数据导出。

1、导出的文件对象的接口

/**
 * 导出的文件对象的接口
 * @author FX_SKY
 */
public interface ExportFileApi {
	
	/**
	 * 导出内容为文件
	 * @param data 示意:需要导出的内容
	 * @return 是否导出成功
	 */
	public boolean export(String data);
	
}



2、接口ExportFileApi的实现

/**
 * 导出成文本文件格式的对象
 * @author FX_SKY
 *
 */
public class ExportTxtFile implements ExportFileApi {

	@Override
	public boolean export(String data) {
		//简单示意一下
		System.out.println("导出数据"+data+"到文本文件");
		return true;
	}
}



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

	@Override
	public boolean export(String data) {
		//简单示意一下
		System.out.println("导出数据"+data+"到数据库备份文件");
		return true;
	}
}



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

/**
 * 实现导出数据的业务功能对象
 * @author FX_SKY
 *
 */
public abstract class ExportOperate {

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



4、加入两个Creator实现

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

/**
 * 具体的创建器实现对象,实现创建导出成文本文件格式的对象
 * @author FX_SKY
 *
 */
public class ExportTxtFileOperate extends ExportOperate {

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

}



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


/**
 * 具体的创建器实现对象,实现创建导出成数据库备份形式的对象
 * @author FX_SKY
 *
 */
public class ExportDBOperate extends ExportOperate {

	@Override
	protected ExportFileApi factoryMethod() {
		// 创建导出成数据库备份形式的对象
		return new ExportDB();
	}

}



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

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//创建需要使用的Creator对象
		ExportOperate operate = new ExportTxtFileOperate();
		//调用导出数据的功能方法
		operate.export("测试数据");
	}

}




工厂方法模式的功能

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

这样在设计的时候,不用去考虑具体的实现,需要某个对象,把它通过工厂方法返回就好了,在使用这些对象实现功能的时候还是通过接口来操作,这类似 Ioc/DI的思想。

工厂方法模式的优缺点

工厂方法模式的优点

可以在不知道具体实现的情况下编程

工厂方法模式的可以让你在实现功能的时候,如果需要某个产品对象,只需要使用产品的接口即可,而无须关系具体的实现。选择具体实现的任务延迟到子类去完成

更容易扩展对象的新版本

工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易。已有的代码都不会改变,只需要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。

连接平行的类层次

工厂方法除了创建产品对象外,在连接平行的类层次上也大显身手。

工厂方法模式的缺点

具体产品对象和工厂方法的耦合性

在工厂方法模式中,工厂方法是需要创建产品对象的,也就是需要选择具体的产品对象,并创建他们的实例,因为具体产品对象和工厂方法是耦合的。

工厂方法模式的本质

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

对设计原则的体现

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

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

何时选用工厂方法模式

建议在以下情况中选用工厂方法模式。

  • 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
  • 如果一个类本身就希望由它的子类来创建所需要的对象的时候,应该使用工厂方法模式。