一、什么是工厂模式?
工厂模式属于创建型设计模式,核心思想是将对象的实例化过程封装到特定方法或类中,让客户端不需要直接通过new
关键字创建对象。
举个例子:就像奶茶店不需要顾客自己调配饮品,而是通过"点单-制作"的流程解耦需求与实现。
// 简单工厂示例:按钮组件生成器
interface Button {
render(): void;
}
class PrimaryButton implements Button {
render() {
console.log("渲染蓝色主按钮");
}
}
class DangerButton implements Button {
render() {
console.log("渲染红色警示按钮");
}
}
// 工厂方法(这里用函数式实现更符合前端习惯)
function createButton(type: 'primary' | 'danger'): Button {
switch(type) {
case 'primary':
return new PrimaryButton();
case 'danger':
return new DangerButton();
default:
throw new Error("未知按钮类型");
}
}
// 客户端调用
const submitBtn = createButton('primary');
submitBtn.render(); // 输出:渲染蓝色主按钮
二、工厂模式的三种形态
- 简单工厂:通过一个方法集中处理所有创建逻辑(适合类型较少的情况)
- 工厂方法:定义抽象工厂接口,由子类决定实例化哪个类(符合开闭原则)
- 抽象工厂:创建相关或依赖对象的家族(适合复杂产品线)
三、核心优势
- 解耦创建逻辑:将易变的创建过程隔离,修改创建逻辑只需调整工厂
- 类型系统友好:配合接口使用能保证返回对象的一致性
- 简化复杂创建:隐藏对象初始化时的复杂依赖关系(如需要多个服务注入)
// 工厂方法模式示例:跨平台UI组件
abstract class Dialog {
abstract createButton(): Button;
render() {
const button = this.createButton();
button.render();
}
}
class WindowsDialog extends Dialog {
createButton() {
return new WindowsButton(); // 假设有具体实现
}
}
class WebDialog extends Dialog {
createButton() {
return new HtmlButton(); // 浏览器环境专用按钮
}
}
// 运行时根据环境选择工厂
const currentDialog = isMobile ? new WebDialog() : new WindowsDialog();
currentDialog.render();
四、典型使用场景
- 组件库开发:根据不同主题生成样式化的组件
- 跨平台适配:为不同运行环境生成对应实现
- 对象池管理:统一管理相似对象的创建和回收
- 配置驱动UI:根据JSON配置动态生成界面元素
五、必须注意的坑
- 过度设计警告:如果类型不超过3个,直接
switch-case
可能更简单 - 类型扩散问题:每新增一个产品就需要对应工厂,可能造成类爆炸
- 调试复杂度:代码执行链路变长,需要跳转多个工厂类
- 实例状态管理:工厂创建的对象如果包含状态需要特别注意生命周期
// 合理使用示例:动态表单控件工厂
class ControlFactory {
private registry = new Map<string, ControlConstructor>();
register(type: string, constructor: ControlConstructor) {
this.registry.set(type, constructor);
}
create(type: string, config: ControlConfig) {
const Constructor = this.registry.get(type);
if (!Constructor) throw new Error(`未注册的控件类型: ${type}`);
return new Constructor(config);
}
}
// 注册自定义控件
factory.register('color-picker', ColorPickerControl);
factory.register('rating', StarRatingControl);
// 根据后端配置动态生成
const configFromAPI = [{ type: 'color-picker', label: '主题色' }];
configFromAPI.forEach(c => {
const control = factory.create(c.type, c);
formContainer.appendChild(control.render());
});
六、性能优化策略
- 对象缓存:对频繁创建且无状态的对象进行复用
- 惰性加载:推迟创建高消耗对象直到真正需要时
- Tree-shaking优化:将不同实现分离到独立模块
// 带缓存的对象池工厂
class ModelPool {
private pool = new Map<string, THREE.Object3D[]>();
get(modelName: string) {
if (!this.pool.has(modelName)) {
this.pool.set(modelName, []);
}
const available = this.pool.get(modelName)!.filter(m => !m.visible);
if (available.length > 0) {
return available[0];
}
const newModel = this.loadModel(modelName);
this.pool.get(modelName)!.push(newModel);
return newModel;
}
private loadModel(name: string) {
// 实际加载3D模型的实现
}
}
七、面试考点解析
当候选人回答该问题时,需重点考察:
- 是否能区分三种工厂模式的应用场景
- 能否识别过度使用工厂模式带来的问题
- 是否具备实际项目中的优化经验
- 对TypeScript类型系统的运用能力
陷阱问题示例:
"假设需要支持插件系统,允许第三方注册新控件类型,工厂模式该如何改进实现?"
期望答案应涉及:
- 注册机制的设计
- 类型安全的实现
- 生命周期管理
- 防冲突处理
八、现代前端框架中的实践
- React Context + 工厂模式:通过Context传递主题工厂
const ThemeContext = createContext<ButtonFactory>(defaultFactory);
function App() {
return (
<ThemeContext.Provider value={materialFactory}>
<FormPage /> // 内部组件使用统一工厂创建元素
</ThemeContext.Provider>
);
}
- Vue组合式API工厂:创建可复用的组件逻辑
export function useFormControl(factory: ControlFactory) {
const controls = ref([]);
function addControl(config) {
controls.value.push(factory.create(config));
}
return { controls, addControl };
}
九、决策流程图
是否需要使用工厂模式?问以下问题:
- 是否需要支持多种实现变体?✅→ 考虑工厂
- 对象创建是否包含复杂初始化?✅→ 需要封装
- 是否需要集中管理对象生命周期?✅→ 适合工厂
- 未来是否可能有新增类型?✅→ 工厂方法更优
十、推荐学习路径
- 从简单工厂开始实践,逐步理解抽象的必要性
- 研究主流UI库的组件创建实现(如Material-UI的withStyles)
- 结合DI容器理解工厂的进阶应用
- 学习相关模式:建造者模式、策略模式、模板方法模式
工厂模式就像代码界的乐高积木生产线,关键在于在灵活性和复杂度之间找到平衡点。
掌握其精髓后,面对多变的需求就能快速搭建出可维护的架构。