简单工厂、工厂方法、抽象方法。
简单工厂 —— Factory.create(any-object-class)
工厂方法 —— Factory → ObjectFactory
抽象方法 —— 不同的 Factory 对于不同的任务有不同的实现方式。DI 容器,基于工厂模式,在程序启动时,根据配置事先创建好对象。当需要时,直接从容器中获取。
- 配置解析(创建哪些对象、什么时候创建对象)、
- 对象创建(反射等)
- 生命周期管理(新对象 or 复用、延迟加载、初始化/销毁钩子)
工厂模式分类:简单工厂、工厂方法、抽象工厂。前两个在项目中应用较多。
1 解决的问题
工厂模式的出发点/目的在于:解决创建对象时过于复杂的问题。
- 复杂的逻辑判断情况
- 复杂的对象依赖情况
复杂的逻辑判断情况
快递运输时,不同情况的货物存在不同的运输方式。例如,不紧急/距离近的货物通过货车运送,紧急的/距离远的货物通过飞机运送。同样类比奶茶店,奶茶店产出不同类型的奶茶。
if type = "cup1": return new Cup1();
else if type = "cup2": return new Cup2();
else ...
// switch-case复杂的对象依赖情况
复杂对象依赖类似于:new A(new B(new C()),new A(new B(), new C())
2 简单工厂(Simple Factory)
** 简单工厂 ** 模式,由 ** 抽象产品 、 具体产品 、 工厂/创建者 ** 三个要素组成,工厂内有具体的逻辑去判断生成怎么样的产品。
2.1 具体实现
产品
// 抽象产品
public abstract class Fruit {}
// 具体产品
public class Apple extends Fruit {}
public class Pear extends Fruit {}工厂
public class FruitFactory {
public Fruit create(String type) {
switch (type) {
case "苹果":
AppleSeed appleSeed = new AppleSeed();
Sunlight sunlight = new Sunlight();
Water water = new Water();
return new Apple(appleSeed, sunlight, water);
case "梨子":
return new Pear();
default:
throw new IllegalArgumentException("暂时没有这种水果");
}
}
}调用者
public class User {
private void eat(){
FruitFactory fruitFactory = new FruitFactory();
Fruit apple = fruitFactory.create("苹果");
Fruit pear = fruitFactory.create("梨子");
apple.eat();
pear.eat();
}
}创建对象复杂的语句可以都扔到工厂里去实现,调用者调用时只需要简单调用就可以。
2.2 简单工厂的弊端
- 如果需要生产的产品过多,此模式会导致工厂类过于庞大,变成超级类。当不同的奶茶生成过程需要修改时,都要修改此类,违背了单一职责原则。
- 当要生产新的产品时,必须在工厂类中添加新的分支。而开闭原则告诉我们:类应该对修改封闭。我们希望在添加新功能时,只需增加新的类,而不是修改既有的类,所以这就违背了开闭原则。
3 工厂方法模式(Factory Method)
工厂方法针对简单工厂弊端,对简单工厂进行改进,由** 抽象产品 、 具体产品 、 抽象工厂 、 具体工厂 ** 四个要素组成。
虽然工厂方法代码量增加了,但是解决了关键问题,同时避免了简单工厂的两种弊端。
3.1 模式结构

3.2 具体实现
苹果工厂
public class AppleFactory {
public Fruit create(){
AppleSeed appleSeed = new AppleSeed();
Sunlight sunlight = new Sunlight();
Water water = new Water();
return new Apple(appleSeed, sunlight, water);
}
}梨子工厂
public class PearFactory {
public Fruit create(){
return new Pear();
}
}调用者:
public class User {
private void eat(){
AppleFactory appleFactory = new AppleFactory();
Fruit apple = appleFactory.create();
PearFactory pearFactory = new PearFactory();
Fruit pear = pearFactory.create();
apple.eat();
pear.eat();
}
}4 适用场景
场景 1:无法预知对象确切类别及其依赖关系。
将创建产品的代码与实际使用产品的代码分离,从而能在不影响其他代码的情况下扩展产品创建部分代码。
需要更改创建情况时,只需要更改工厂类及其子类即可,无需更改其他内容。
场景 2:用户能扩展内部组件。
场景 3:复用现有对象来节省系统资源,类似工具类。
在处理大型资源密集型对象 (比如数据库连接、文件系统和网络资源) 时,你会经常碰到这种资源需求。
- 首先,你需要创建存储空间来存放所有已经创建的对象。
- 当他人请求一个对象时,程序将在对象池中搜索可用对象。
- …然后将其返回给客户端代码。
- 如果没有可用对象,程序则创建一个新对象 (并将其添加到对象池中)。
可能最显而易见,也是最方便的方式,就是将这些代码放置在我们试图重用的对象类的构造函数中。但是从定义上来讲,构造函数始终返回的是新对象,其无法返回现有实例。
因此,你需要有一个既能够创建新对象,又可以重用现有对象的普通方法。这听上去和工厂方法非常相像。
5 优缺点
优点:
- 你可以避免创建者和具体产品之间的紧密耦合。
- 单一职责原则。你可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
- 开闭原则。无需更改现有客户端代码,你就可以在程序中引入新的产品类型。
缺点:
- 应用工厂方法模式需要引入许多新的子类,代码可能会因此变得更复杂。最好的情况是将该模式引入创建者类的现有层次结构中。
6 TODO
把书读薄 | 《设计模式之美》设计模式与范式(创建型-工厂模式) - 掘金
工厂模式与 DI 容器的关系
DI 容器底层最基本的设计思路就是基于工厂模式的,相当于一个大工厂,负责在程序启动时,根据配置(创建哪些类对象、对象创建需要依赖哪些其他类对象) 事先创建好对象。当程序需要用到某个类对象时,直接从容器中获取即可。正是因为它持有一堆对象,所以这个框架才被称为 “容器”。
DI 容器的三个核心功能:
- ① 配置解析:通过一种形式让应用告知 DI 容器要创建哪些对象,一般通过配置文件形式,如 Spring 的配置文件;
- ② 对象创建:DI 容器根据配置文件提供的信息创建对象,Java 中一般通过反射来动态加载类、创建对象;
- ③ 生命周期管理:返回新对象还是复用、是否延迟加载、是否配置对象初始化/销毁的钩子等;
课程中写了一个实现简单 DI 容器的例子,比较简单,只列下流程:最小原型设计、提供执行入口、配置文件解析、核心工厂类设计。