工厂方法是指使用一个接口方法代替 new 操作实现对象实例化,隐藏对象创建的细节和逻辑,这样可以根据不同的场景返回不同的实例对象。
工厂方法根据抽象程度不同分为两类:
- 简单工厂,又称静态工厂
- 工厂方法,又称多态工厂、虚拟构造器
一、简单工厂
定义一个专门的工厂类负责创建其它类的实例,被创建的实例拥有共同的父类。特点:
- 有一个抽象产品类,定义了一个以上的抽象方法需要在具体产品子类中实现
- 继承自该抽象产品类的具体产品子类需实现这些抽象方法
- 有一个工厂类,专门负责实例化这些子类对象
// 抽象基类:颜色
public abstract class Color {
// 抽象方法:返回色值
public abstract String getValue();
}
// 子类:黑色
public class Black extends Color {
public Black() {
System.out.println("Black Instantiation");
}
@Override
public String getValue() {
return "000000";
}
}
// 子类:红色
public class Red extends Color {
public Red() {
System.out.println("Red Instantiation");
}
@Override
public String getValue() {
return "ff0000";
}
}
// 子类:白色
public class White extends Color {
public White() {
System.out.println("White Instantiation");
}
@Override
public String getValue() {
return "ffffff";
}
}
// 实例化颜色的工厂类
public class ColorFactory {
public static Color getColor(String colorName) {
switch (colorName.toLowerCase()) {
case "black":
return new Black();
case "red":
return new Red();
case "white":
return new White();
default:
return null;
}
}
}
测试简单工厂
import org.junit.Test;
import java.util.Random;
public class ColorFactoryTests {
@Test
public void test() {
String[] colorNames = {"black", "red", "white"};
Random random = new Random();
for (int i = 0; i < 10; i++) {
ColorFactory.getColor(colorNames[random.nextInt(3)]);
}
}
}
测试结果
Black Instantiation
Black Instantiation
White Instantiation
Red Instantiation
White Instantiation
Red Instantiation
White Instantiation
Black Instantiation
White Instantiation
White Instantiation
优点:
- 使用者通过工厂获取所需对象实例,无需直接创建对象实例,这样使用者就无需关心创建过程,只需要聚焦于使用,实现了解耦;
- 工厂负责相关对象的实例化工作,如果创建对象的逻辑发生变化,则只需在工厂中集中修改,便于维护。
缺点:
- 工厂集中了对象的实例化工作,则一定要保证其始终正常运行,否则系统中所有相关对象的实例化都会发生异常;
- 如果对象类型很多,则工厂逻辑会比较复杂,且一旦新增一个对象类型,就需要修改工厂的逻辑,违反了“开-闭原则”;
- 使用了静态工厂方法,静态方法不能被集成和重写,工厂角色无法形成继承结构。
二、工厂方法
工厂方法定义一个用于创建对象的工厂接口,让工厂子类(具体工厂)决定实例化哪一个类。工厂方法使一个类的实例化延迟到工厂子类。特点:
- 定义一个抽象产品类,被所有具体产品类继承并实现公共行为方法
- 定义一个工厂接口,此接口包含创建抽象产品类的方法,具体工厂类实现此接口方法
- 具体工厂类决定创建具体产品实例的逻辑
// 工厂接口
public interface IColorFactory {
Color getColor();
}
// 具体工厂类:实例化黑色
public class BlackFactory implements IColorFactory {
@Override
public Color getColor() {
return new Black();
}
}
// 具体工厂类:实例化红色
public class RedFactory implements IColorFactory {
@Override
public Color getColor() {
return new Red();
}
}
// 具体工厂类:实例化白色
public class WhiteFactory implements IColorFactory {
@Override
public Color getColor() {
return new White();
}
}
// Black、Red、White 三个类的代码在此省略,和简单工厂中示例代码完全一致
工厂方法的使用与测试
@Test
public void test() {
IColorFactory factory;
factory = new BlackFactory();
Color black = factory.getColor();
factory = new RedFactory();
Color red = factory.getColor();
factory = new WhiteFactory();
Color white = factory.getColor();
}
优点:
- 工厂方法中不再使用核心工厂类负责所有相关对象的创建,而是使用接口让其变成了一个抽象角色,定义了所有工厂子类必须实现的接口方法,这样便可以在不修改现有工厂的情况下引入新产品,遵守了“开-闭原则”并提高了可扩展性;
- 在工厂方法中,通常一个核心工厂接口对应一个抽象产品基类,每个具体工厂类对应一个具体产品类,这个具体工厂就负责这个具体产品的实例化。
缺点: 由于具体工厂类和具体产品类一一对应,所以产品类数量的增加会导致工厂类数据同比例增加,可能导致类规模迅速扩大,增加系统编译和运行开销,也不利于系统维护。