简单工厂模式
工厂模式:工厂方法模式;
低阶:简单工厂模式;
高阶:抽象工厂模式;
1)概述
定义一个工厂类,根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
在简单工厂模式中创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式。
2)流程
1.将需要创建的对象(Chart对象)的代码封装到不同的类中,称为具体产品类;
2.将公共的代码进行抽象和提取后封装在一个抽象产品类中,每一个具体产品类都是抽象产品类的子类;
3.提供一个工厂类用于创建各种产品,在工厂类中提供一个创建产品的工厂方法,该方法可以根据所传入的参数不同创建不同的具体产品对象;
4.客户端只需调用工厂类的工厂方法并传入相应的参数即可得到一个产品对象。
3)角色
● Factory(工厂角色):即工厂类,负责创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法 factoryMethod(),返回类型为抽象产品类型 Product。
● Product(抽象产品角色):是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
● ConcreteProduct(具体产品角色):是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。
4)案例-初始方案
开发一套图表库,为应用系统提供不同外观的图表,例如柱状图、饼状图、折线图等,要求可以方便增加新类型的图表。
class Chart {
private String type; //图表类型
public Chart(Object[][] data, String type) {
this.type = type;
if (type.equalsIgnoreCase("histogram")) {
//初始化柱状图
}else if (type.equalsIgnoreCase("pie")) {
//初始化饼状图
}else if (type.equalsIgnoreCase("line")) {
//初始化折线图
}
}
public void display() {
if (this.type.equalsIgnoreCase("histogram")) {
//显示柱状图
}else if (this.type.equalsIgnoreCase("pie")) {
//显示饼状图
}else if (this.type.equalsIgnoreCase("line")) {
//显示折线图
}
}
}
问题:
(1) 在Chart类中包含很多"if...else..."代码块,整个类的代码冗长,阅读难度、维护难度和测试难度大,影响系统性能,程序在执行过程中需要做大量的条件判断。
(2) Chart类的职责过重,它负责初始化和显示所有的图表对象,将各种图表对象的初始化代码和显示代码集中在一个类中实现,违反了"单一职责原则",不利于类的重用和维护;而且将大量的对象初始化代码都写在构造函数中将导致构造函数非常庞大,对象在创建时需要进行条件判断,降低了对象创建的效率。
(3) 当需要增加新类型的图表时,必须修改Chart类的源代码,违反了"开闭原则"。
(4) 客户端只能通过new关键字来直接创建Chart对象,Chart类与客户端类耦合度较高,对象的创建和使用无法分离。
(5) 客户端在创建Chart对象之前可能还需要进行大量初始化设置,例如设置柱状图的颜色、高度等,如果在Chart类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设置,这些代码在每次创建Chart对象时都会出现,导致代码的重复。
5)案例-重构后
//抽象图表接口:抽象产品类
interface Chart {
public void display();
}
//柱状图类:具体产品类
class HistogramChart implements Chart {
public HistogramChart() {
System.out.println("创建柱状图!");
}
public void display() {
System.out.println("显示柱状图!");
}
}
//饼状图类:具体产品类
class PieChart implements Chart {
public PieChart() {
System.out.println("创建饼状图!");
}
public void display() {
System.out.println("显示饼状图!");
}
}
//折线图类:具体产品类
class LineChart implements Chart {
public LineChart() {
System.out.println("创建折线图!");
}
public void display() {
System.out.println("显示折线图!");
}
}
//图表工厂类:工厂类
class ChartFactory {
//静态工厂方法
public static Chart getChart(String type) {
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;
}
}
// 客户端
class Client {
public static void main(String args[]) {
Chart chart;
chart = ChartFactory.getChart("histogram"); //通过静态工厂方法创建产品
chart.display();
}
}
6)案例-使用配置文件优化
将静态工厂方法的参数存储在XML或properties格式的配置文件中。
<?xml version="1.0"?>
<config>
<chartType>histogram</chartType>
</config>
通过工具类 XMLUtil 读取配置文件中的字符串参数。
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
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("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;
}
}
}
引入配置文件和工具类XMLUtil后,客户端修改如下:
class Client {
public static void main(String args[]) {
Chart chart;
String type = XMLUtil.getChartType(); //读取配置文件中的参数
chart = ChartFactory.getChart(type); //创建产品对象
chart.display();
}
}
7)总结
简单工厂模式提供了工厂类用于创建对象,将对象的创建和对象的使用分开。
1.优点
实现了对象创建和使用的分离。
引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类。
2.缺点
工厂类集中了所有产品的创建逻辑,职责过重。
系统扩展困难,添加新产品需要修改工厂逻辑。
产品类型较多时,可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
简单工厂模式使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
3.适用场景
工厂类负责创建的对象较少。
客户端只了解传入工厂类的参数,对于如何创建对象并不关心。