第3章 简单工厂模式
3.1 简单工厂模式概述
考虑一个水果农场,当用户需要某一种水果时该农场能够根据用户所提供的水果名称返回该水果。在此,水果农场被称为工厂(Factory),而生成的水果被称为产品(Product),水果的名称则被称为参数,工厂可以根据参数的不同返回不同的产品。
简单工厂模式(Simple Factory Pattern):定义一个工厂类,可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
3.2 简单工厂模式结构与实现
3.2.1 简单工厂模式结构
- Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法 factoryMethod(),它的返回类型为抽象产品类型Product。
- Product(抽象产品角色):它是工厂类创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法﹐因为所有创建的具体产品对象都是其子类对象。
- ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。
3.2.2 简单工厂模式实现
抽象类:
public abstract class Product {
//所有产品的公共业务方法
public void methodSame() {
//公共方法的实现
}
//声明抽象业务方法
public abstract void methodDiff();
}
具体产品类:
public class ConcreteProduct extends Product{
//实现业务方法
@Override
public void methodDiff() {
}
}
工厂类:
public class Factory {
//静态工厂方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalsIgnoreCase("A")) {
product = new ConcreteProductA();
//初始化设置product
} else if (arg.equalsIgnoreCase("B")) {
product = new ConcreteProductB();
//初始化设置product
}
return product;
}
}
public class Client {
public static void main(String[] args) {
Product product;
product = Factory.getProduct("A");//通过工厂类创建产品对象
product.methodSame();
product.methodDiff();
}
}
3.2.3 简单工厂模式应用举例
实例说明
实例类图
- 抽象产品类:Chart
- 具体产品类:
- HistogramChart
- PieChart
- LineChart
- 工厂类:ChartFactory
实例代码
Chart:抽象图表接口,充当抽象产品类
package designpatterns.simplefactory;
public interface Chart {
public void display();
}
HistogramChart:柱状图类,充当具体产品类
package designpatterns.simplefactory;
public class HistogramChart implements Chart {
public HistogramChart() {
System.out.println("创建柱状图!");
}
@Override
public void display() {
System.out.println("显示柱状图!");
}
}
PieChartChart:饼状图类,充当具体产品类
package designpatterns.simplefactory;
public class PieChart implements Chart {
public PieChart() {
System.out.println("创建饼状图!");
}
@Override
public void display() {
System.out.println("显示饼状图!");
}
}
LineChartChart:饼状图类,充当具体产品类
package designpatterns.simplefactory;
public class LineChart implements Chart {
public LineChart() {
System.out.println("创建折现图!");
}
@Override
public void display() {
System.out.println("显示折现图!");
}
}
ChartFactory:图表工厂类,充当工厂类
package designpatterns.simplefactory;
public class ChartFactory {
//静态工厂方法
public static Chart getChart(String type) throws NullPointerException {
Chart chart = null;
if (type.equalsIgnoreCase("histogram")) {
chart = new HistogramChart();
System.out.println("初始化设置柱状图!");
} else if (type.equalsIgnoreCase("pie")) {
chart = new PieChart();
System.out.println("初始化设置饼状图! ");
} else if (type.equalsIgnoreCase("line")) {
chart = new LineChart();
System.out.println("初始化设置折现图! ");
}
return chart;
}
}
Client:客户端测试类
package designpatterns.simplefactory;
public class Client {
public static void main(String[] args) {
Chart chart;
chart = ChartFactory.getChart("histogram");
chart.display();
}
}
结果及分析
3.3 修改
在创建具体Chart对象时必修通过修改客户端代码中静态工厂方法参数来更换具体产品对象,客户端代码需要重新编译,对于客户端违反了开闭原则。
config.xml
<?xml version="1.0"?>
<config>
<chartType>histogram</chartType>
</config>
XMLUtil
package designpatterns.simplefactory;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
public class XMLUtil {
//该方法用于从XML配置文件中提取图表类型,并返回类型名
public static String getChartType() {
try {
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src//designpatterns//simplefactory//config.xml"));
//获取包含图表类型的文本结点
NodeList nl = doc.getElementsByTagName("chartType");
Node classNode = nl.item(0).getFirstChild();
String chartType = classNode.getNodeValue().trim();
return chartType;
} catch (
Exception e) {
e.printStackTrace();
return null;
}
}
}
Client
package designpatterns.simplefactory;
public class Client {
public static void main(String[] args) {
Chart chart;
String type = XMLUtil.getChartType();//读取配置文件中的参数
chart = ChartFactory.getChart(type);
chart.display();
}
}
3.4 关于创建对象与使用对象
面向对象软件系统一个与对象相关的职责通常有三种:
-
对象本身的职责
-
创建对象的职责
-
使用对象的职责
-
对象本身的职责:对象本身所具有的一些数据和行为,可通过公开的(public)来实现。
-
创建对象的职责:
- new创建对象
- 反射机制创建对象(第4章)
- 克隆方法创建对象(第7章)
- 工厂类创建对象
所有工厂模式都强调一点:
两个类A和B之间的关系仅仅是A创建B或是A使用B。
3.5 简单工厂的简化
将抽象产品类和工厂类合并,并将静态工厂方法移至抽象产品类中。
3.6 简单工厂模式优/缺点与适用环境
3.6.1 简单工厂模式优点
- 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度上减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
3.6.2 简单工厂模式缺点
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式势必会增加系统中类的个数(引人了新的工厂类),增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
3.7.3 简单工厂模式适用环境
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。