工厂模式关注的不是消灭 new,而是把对象创建规则放到合适的位置,让调用者依赖稳定的接口。这份笔记沿用《Head First 设计模式》中的学习线索,区分三种常见写法。
简单工厂:集中选择逻辑
假设多个地方都根据类型创建 Apple 或 Banana,可以把分支集中到一个入口:
Fruit create(String kind) {
if ("apple".equals(kind)) return new Apple();
if ("banana".equals(kind)) return new Banana();
throw new IllegalArgumentException("Unknown kind: " + kind);
}
这里 Fruit、Apple、Banana 为示意类型。调用者不用各写一遍分支,但新增品种通常仍要改这个入口。简单工厂可以理解为集中创建的惯用结构,不必为了名称争论忽略其实际作用。
工厂方法:把创建交给扩展点
创建者声明返回产品接口的工厂方法,子类决定具体创建哪一种产品。创建者的其他流程可以调用这个扩展点,而不直接绑定具体产品。
例如一个处理水果订单的抽象流程调用 createFruit(),不同子类分别创建苹果或香蕉。新增创建者可以扩展实现,但系统如何选择创建者、怎样注册或装配,仍需要安排,并非整个应用都绝对不用修改。
抽象工厂:替换一组相关产品
如果每个地区工厂都生产苹果和香蕉,可以定义:
interface FruitFactory {
Apple createApple();
Banana createBanana();
}
南方工厂和北方工厂各自实现接口,分别返回所属产品族中的苹果与香蕉。调用者只依赖 FruitFactory,就能整体替换一组相关产品;这里的 Apple 和 Banana 也应是相应的产品抽象。
关键不只是接口里有几个创建方法,而是这些产品作为一个族,是否需要一致地选择。
两种扩展的代价不同
增加“中部产品族”,通常是新增一套工厂和产品实现;增加“梨”这个产品种类,却要扩展工厂接口,并让已有工厂都支持 createPear()。
所以抽象工厂擅长的是在既定产品结构下增加产品族,不是对所有变化都零修改。选择结构前,应先判断更常发生的是族的替换,还是产品种类的增加。
依赖倒置也不是“任何变量都不能引用具体类”的禁令。创建本身总有地方需要知道实现;只需把这份知识集中到合适边界。工厂还不自动提供单例、缓存、对象池或资源释放,这些生命周期策略应另行定义。