工厂方法模式-factory method

1.工厂方法模式定义

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

工厂方法模式的解决思路: 那就是不解决,采取无为而治的方式;

2.工厂方法模式的结构和说明

工厂方法模式的结构如图:

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

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

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

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

3.工厂方法的示例代码

/**
 * 工厂方法所创建的对象的接口
 * @author Administrator
 *
 */
public interface Product {
	//可以定义Product的属性和方法
}
/**
 * 具体的Product对象
 * @author Administrator
 *
 */
public class ConcreteProduct implements Product
{

}

/**
 * 创建器,声明工厂方法
 * @author Administrator
 *
 */
public abstract class Creator {
	/**
	 * 创建Product的工厂方法
	 * @return
	 */
	protected abstract Product factoryMethod();
	/**
	 * 实现某些功能的方法
	 */
	public void someOperation() {
		//通常在这些方法实现中需要调用工厂方法来获取Product产品
		Product product = factoryMethod();
	}

}

/**
 * 具体的创建器实现对象
 * @author Administrator
 *
 */
public class ConcreteCreator extends Creator{
	@Override
	protected Product factoryMethod() {
		// 重定义工厂方法,返回一个具体的Product对象
		return new ConcreteProduct();
	}

}

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

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

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

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

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

考虑这样的一个实际应用:实现一个导出数据的应用框架,来让客户选择数据的导出方式;并真正执行数据的导出,通常这种系统在导出数据上会有一些约定的方式,比如导出成文本格式、数据库备份形式、Excel 格式、Xml格式等。

(1) 导出接口

(2) 实现导出PDF的示例代码:

(3) 实现导出Excel的示例代码:

(4) 实现ExportOperate 的示例代码如下:

(5) 加入了两个Creator实现

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


/**
 * 导出的文件对象的接口
 * @author Administrator
 *
 */
public interface ExportFileApi {
	/**
	 * 导出内容成为文件
	 * @param data 需要保存的数据
	 * @return 导出是否成功
	 */
	public boolean export(String data);

}

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

	@Override
	public boolean export(String data) {
		System.out.println("导出数据"+data+"到Excel文件中!!");
		return true;
	}
}
/**
 * 导出成数据库备份文件形式的对象
 * @author Administrator
 *
 */
public class ExportPDFFile implements ExportFileApi{
	@Override
	public boolean export(String data) {
		System.out.println("导出数据"+data+"到PDF文件!!");
		return true;
	}
}
/**
 * 实现导出数据的业务功能对象
 * @author Administrator
 *
 */
public abstract class ExportOperate {
	/**
	 * 导出文件
	 * @param data 需要保存的数据
	 * @return 是否成功导出文件
	 */
	public boolean export(String data) {
		//使用工厂方法
		ExportFileApi api = factoryMethod();
		return api.export(data);
	}
	
	/**
	 * 工厂方法,创建导出的文件对象的接口对象
	 * @return
	 */
	protected abstract ExportFileApi factoryMethod();
}
/**
 * 具体的创建器实现对象,实现创建导出成PDF格式的对象
 * @author Administrator
 *
 */
public class ExportPDFFileOperate extends ExportOperate
{

	@Override
	protected ExportFileApi factoryMethod() {
		//创建导出成PDF格式的文件
		return new ExportPDFFile();
	}
}

/**
 * 具体的创建器实现对象,实现创建导出成Excel格式的对象
 * @author Administrator
 *
 */
public class ExportExcelFileOperate extends ExportOperate
{

	@Override
	protected ExportFileApi factoryMethod() {
		//创建导出成Excel格式的文件
		return new ExportExcelFile();
	}
}


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

}

5.参数化工厂方法

所谓参数化工厂方法指的就是: 通过给工厂方法传递传递参数,然工厂方法根据参数的不同来创建不同的产品对象

使用参数化工厂方法实现导出文件功能:

/**
 * 实现导出数据的业务功能对象
 * @author Administrator
 *
 */
public  class ExportOperate {
	/**
	 * 导出文件
	 * @param data 需要保存的数据
	 * @return 是否成功导出文件
	 */
	public boolean export(int type , String data) {
		//使用工厂方法
		ExportFileApi api = factoryMethod(type);
		return api.export(data);
	}
	
	/**
	 * 工厂方法,创建导出的文件对象的接口对象
	 * @return
	 */
	protected  ExportFileApi factoryMethod(int type) {
		ExportFileApi api = null;
		
		if(type == 1) {
			api =  new ExportPDFFile();
		}else if(type == 2) {
			api =  new ExportExcelFile();
		}
		return api;
	}
}

测试客户端:

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

}

使用参数化工厂方法,扩展起来会非常容易,已有的代码不会改变,只需要加入一个子类来提供新的工厂来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。

扩展一个导出成xml文件的示例代码如下:

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

然后扩展ExportOperate 类,来加入新的实现。示例代码如下:

/**
 * 实现导出数据的业务功能对象
 * @author Administrator
 *
 */
public  class ExportOperate2 extends ExportOperate{
	/**
	 * 导出文件
	 * @param data 需要保存的数据
	 * @return 是否成功导出文件
	 */
	public boolean export(int type , String data) {
		//使用工厂方法
		ExportFileApi api = factoryMethod(type);
		return api.export(data);
	}
	
	/**
	 * 工厂方法,创建导出的文件对象的接口对象
	 * @return
	 */
	protected  ExportFileApi factoryMethod(int type) {
		ExportFileApi api = null;
		
		if(type == 3) {
			api =  new ExportPDFFile();
		}else {
			api = super.factoryMethod(type);
		}
		return api;
	}
}

工厂方法模式的优缺点

优点:

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

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

  • 更容易扩展对象的新版本

工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易。比如,上面示例的参数化工厂方法实现中,扩展一个新的导出xml文件格式的实现,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。

缺点:

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

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

6.工厂方法模式对设计原则的体现

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

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

比如前面的示例,实现客户端请求操作的ExportOperator 就是高层组件;而具体实现数据导出的对象就是底层组件,比如ExportPDFFile、ExportExceFile;而ExportFileApi 接口就相当于是那个抽象。

对于ExportOperate 老说,它不关心具体的事项暗示,它只是"面向接口编程";对于具体的实现来说,它只关心自己“如何实现接口”所要求的功能。

那么倒置的是什么呢?倒置的是这个接口的"所有权"。事实上,ExportFileApi 接口中定义的功能,都是由高层组件ExportOperate来提出 的 要求,也就是说接口中的功能,是高层组件需要的功能。但是高层组件只是提出要求,并不关心如何实现,而底层组件,就是来真正实现高层组件所要求的接口功能的。因此看起来,底层实现的接口的所有权并不在底层组件手中,而是倒置到高层组件去了。

7.何时选用工厂方法模式

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

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

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

工厂方法模式-factory method_第1张图片

你可能感兴趣的:(java设计模式)