简介
简单工厂模式 Simple Factory Pattern 也叫静态工厂方法模式 Static Factory Method Pattern,隶属于设计模式中的创建型模式。简单工厂通过一个静态方法来给使用方提供类创建的方式,将对象创建的控制权由使用方转换到提供方,可以将使用方和提供方具体的实现解耦,提供方就可以在用户无感知的情况下进行具体实现的替换。
角色
简单工厂模式涉及到3个角色,分别是工厂角色、抽象产品角色、具体产品角色。具体产品是抽象产品的某个实现。
这里的产品也是很抽象的一个概念。按照平时的实践,抽象产品角色可以是策略模式,具体产品角色就是某个策略;抽象产品可以是命令模式,具体产品角色就是某个命令;抽象产品角色也可以是创建型模式中的抽象工厂,具体产品角色就是某个具体的工厂类。只要涉及到继承关系,简单工厂模式就有可能的用武之地,而设计模式是继承使用的重场景,所以简单工厂模式是可以和设计模式的其他模式很好的结合起来的。
模式结构
简单工厂模式通常是以参数来决定我们创建的类的类型的,参数的使用完全取决于简单工厂模式的提供方。我们以策略模式为例,在排序场景使用简单工厂模式来做模式结构说明。
// 抽象产品角色,此处为策略模式接口
public interface Sort {
public void sort(List<Comparable<T>> list);
}
// 具体产品角色,此处为两个排序的具体实现
public class BubbleSort implements Sort {
public void sort(List<Comparable<T>> list) {
//使用冒泡算法对 list 进行排序
}
}
public class QuickSort implements Sort {
public void sort(List<Comparable<T>> list) {
// 使用快排算法对 list 进行排序
}
}
// 工厂角色
// 我们知道在待排数据量很大时使用快排效果最好
// 在数据量小的时候,使用冒泡、插入排序算法效果最好
// 因此我们根据数据量 size 来决定使用哪种排序算法
public class SortFactory {
public static int THRESHOLD = 16;
public static Sort getSortInstance(int size) {
if (size <= THRESHOLD) {
return new BubbleSort();
}
return QuickSort();
}
}
使用场景
使用场景决定于模式解决的问题,简单工厂模式要解决的是对象的实例化问题,因此只要有实例化的场景就有可能用到简单工厂模式,像在角色这一节中已经介绍的几个例子,但是也要注意简单工厂模式最好符合以下的条件:
- 由于创建过程有简单工厂负责,因此具体产品的数量不宜过多,过多的话就会导致简单工厂模式成为修改瓶颈,也不利于维护
- 客户端不需要了解被创建类的细节,而只通过抽象产品角色即可工作
- 对类的数量没有限制,在一些客户端上,有时需要限制包大小,简单工厂模式会多创建一个类出来,需要考虑。
最佳实践
- 简单工厂模式很多时候都是一个简单的参数比较,如下:
public class SimpleFactory {
public static Product getInstance(int param) {
if (param == 1) {
return new ConcreteProduct1();
}
if (param == 2) {
return new ConcreteProduct2();
}
return null;
}
}
在只有简单的几个实现时,这样写还可以接受,但是当新增实现类时,我们就需要修改简单工厂方法的代码,这违背了设计原则中的开闭原则,对这种情况我们可以使用查表法。
public class SimpleFactory {
public static Map<Integer, Product> productMap = new HashMap<>();
static {
productMap.put(1, new ConcreteProduct1());
productMap.put(2, new ConcreteProduct2());
}
public static Product getInstance(int param) {
return productMap.get(param);
}
}
注意,这个实现和上面的实现并不完全一致,因为在每次查表时返回的都是同一个实例,这适合于 Service 等无状态的类,如果需要每次都需要返回一个新的实例,可以使用 Map<Integer, Class<Product>>
的形式,在每次调用时使用newInstance
来获取新实例。
- 简单工厂不仅限于简单的参数相等比较,像我在上面举的排序的例子,简单工厂是可以实现某些策略的。不要自己局限住了。
- 使用外部配置文件的方法来配合简单工厂。很多时候简单工厂的参数和实例的关系是直接写在代码中,或者在一个 map 中,但如果需要将工厂的实例对应关系变为可配置的,由使用方决定采用哪个类,我们就可以将配置关系放到配置文件中,简单工厂读取配置文件来加载对应关系。