第一章 简单工厂模式
一、设计图表库
设计一个图表库,用于为系统提供各种不同外观的图表,如柱状图、饼状图、折线图等。
1.1 初步设计
public class Chart {
private String type; // 图表类型
public Chart(Object[][] data, String type) {
this.type = type;
if (type.equalsIgnoreCase("histogram")) {
// 初始化柱状图
}
if (type.equalsIgnoreCase("pie")) {
// 初始化饼状图
}
if (type.equalsIgnoreCase("line")) {
// 初始化折线图
}
}
public void display() {
if (type.equalsIgnoreCase("histogram")) {
// 显示柱状图
}
if (type.equalsIgnoreCase("pie")) {
// 显示饼状图
}
if (type.equalsIgnoreCase("line")) {
// 显示折线图
}
}
}
1.2 存在的问题
该类在设计时存在如下几个问题:
- Chart 类中包含许多 if…else 代码块,代码冗长,难以阅读、维护和测试;大量的条件判断还将影响系统的性能
- Chart 类职责过重,负责初始化和显示所有图表对象。违反了“单一职责原则”,不利于类的重用和维护;而且将大量的对象初始化代码写在构造函数中,在初始化对象时需要条件判断,降低了对象创建的效率
- 违反了“开闭原则”,当需要增加新类型的图表时,必须修改 Chart 对象的代码
- 客户端只能通过 new 关键字来直接创建 Chart 对象,与客户端耦合度较高,对象的创建和使用无法分离
- 客户端中缺少其它初始化设置,如果在客户端对图表的颜色、高度等进行设置,则会在每次创建对象时都会出现,导致代码的重复
1.3 解决思路
引入工厂类,进行抽象与拆分
二、简单工厂模式
2.1 设计流程
- 将需要创建的各种不同对象的相关代码封装到不同的类中,这些类称为具体产品类
- 将它们公共的代码进行抽象和提取后封装在一个抽象产品类中,每个具体产品类都是抽象产品类的子类
- 提供一个工厂类用于创建各种产品,在工厂类中提供一个创建产品的工厂方法,该方法可以根据所传入的参数不同创建不同的具体对象
- 客户端只需调用工厂类的方法并传入相应的参数即可得到一个产品对象
2.2 简单工厂模式模式的定义
定义一个工厂类,可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,
因此简单工厂模式又被称为静态工厂模式,属于类创建型模式。
2.3 简单工厂模式中的几个角色
- Factory(工厂角色)
- 即工厂类,是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;
- 工厂类可以被外界直接调用,创建所需的产品对象;
- 在工厂类中提供了静态的工厂方法,返回类型为抽象产品类;
- Product(抽象产品角色)
- 是工厂类所创建的对象的父类,封装各产品对象的公共方法
- 抽象产品类的引入,提供了系统的灵活性,使得在工厂类只需定义一个通用的工厂方法,因为所创建的具体产品对象都是其子类对象
- ConcreteProduct(具体产品角色)
- 是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个类的实例。
- 每个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法
三、具体实现
3.1 产品抽象类
/**
* 使用简单工厂模式时,需要先对产品类进行重构
* 根据实际情况设计一个产品层次结构,将所有产品类公共的代码移至抽象类,并声明一些抽象方法供具体产品类类实现
*/
public interface Chart {
void display();
}
3.2 具体产品类
/**
* 具体产品类实现了抽象产品类中声明的抽象业务方法,不同的具体产品类可以提供不同的实现
*/
public class HistogramChart implements Chart {
public HistogramChart() {
System.out.println("生成柱状图");
}
@Override
public void display() {
System.out.println("显示柱状图");
}
}
public class LineChart implements Chart {
public LineChart() {
System.out.println("生成折线图");
}
@Override
public void display() {
System.out.println("显示折线图");
}
}
public class PieChart implements Chart {
public PieChart() {
System.out.println("生成饼状图");
}
@Override
public void display() {
System.out.println("显示饼状图");
}
}
3.3 工厂方法类
/**
* 简单工厂模式的核心就是工厂类
* 通过工厂类的静态工厂方法来创建具体产品对象,而非直接通过new来创建产品对象
*/
public class ChartFactory {
public static Chart getChart(String type) {
switch (type) {
case "histogram":
return new HistogramChart();
case "pic":
return new PieChart();
case "line":
return new LineChart();
default:
return null;
}
}
}
3.4 客户端测试
/**
* 客户端测试代码
*/
public class DemoTest {
public static void main(String[] args) {
Chart pie = ChartFactory.getChart("pie");
pie.display();
}
}
四、总结
简单工厂模式提供了专门的工厂类用于创建对象,将对象的创建和对象的使用分离。
4.1 优点
- 工厂类包含必要的判断逻辑,可以决定何时创建哪一个实例。免除客户端直接创建产品对象的职责,而仅仅“消费”产品。实现了对象创建和使用的分离
- 客户端无需知道所创建的具体产品类的类名,只需要知道具体参数即可。
- 可以将具体参数引入配置文件,在不修改客户端代码的情况下增删具体产品类,在一定程序上提供了系统灵活度。
4.2 缺点
- 工厂类的职责过重,集中了所有产品的创建逻辑。一旦工厂类出现问题,整个系统都会受到影响
- 使用简单工厂模式势必会增加系统中类的个数,增加了系统的复杂度和理解难度
- 系统拓展难度,新增产品时必须修改工厂逻辑,在产品类型较多时,可能造成工厂逻辑过于复杂,不利于系统扩展和维护
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构
4.3 适用场景
- 工厂类负责创建的对象较少,因为需要创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心