🏭 深度解析工厂设计模式:解耦对象创建,让代码更优雅
前言
在日常开发中,我们经常需要根据不同条件创建不同的对象。最直接的方式是在代码中到处使用 new
关键字,但这样会带来严重的耦合问题。工厂设计模式正是为了解决这个问题而生的------它让我们的代码不再直接依赖具体的类,而是通过工厂来创建对象,从而实现解耦。
🤔 为什么需要工厂模式?
问题:直接使用 new 的痛苦
想象你正在开发一个数据可视化应用,需要根据用户选择创建不同类型的图表:
javascript
// ❌ 糟糕的做法:到处都是 new,强耦合
function createChart(type, data) {
let chart;
if (type === 'line') {
chart = new LineChart(data);
chart.setAxisConfig({ x: 'time', y: 'value' });
chart.enableTooltip();
} else if (type === 'bar') {
chart = new BarChart(data);
chart.setColors(['#ff6384', '#36a2eb']);
chart.enableAnimation();
} else if (type === 'pie') {
chart = new PieChart(data);
chart.enableLegend();
chart.setLabelFormat('percentage');
}
return chart;
}
// 使用时
const lineChart = createChart('line', salesData);
const barChart = createChart('bar', revenueData);
这种做法的问题:
- 强耦合 :
createChart
函数直接依赖所有具体的图表类 - 难以扩展 :每增加一种图表类型,都要修改
createChart
函数 - 违反开闭原则:对修改开放,对扩展封闭
- 代码重复:类似的创建逻辑可能散布在多个地方
- 测试困难:难以模拟和替换具体的图表实现
解决方案:工厂模式的核心思想
工厂模式的核心是 "不要直接 new,让工厂来创建"。它通过以下方式解决上述问题:
- 解耦对象创建:客户端不直接依赖具体类
- 封装创建逻辑:复杂的对象创建过程被隐藏在工厂内部
- 统一创建接口:提供一致的对象获取方式
- 易于扩展:新增产品类型无需修改客户端代码
🏗️ 工厂模式的实现
简单工厂模式
最基础的工厂实现,适合产品类型相对固定的场景:
javascript
// 产品基类
class Chart {
constructor(data) {
this.data = data;
}
render() {
throw new Error('子类必须实现 render 方法');
}
}
// 具体产品
class LineChart extends Chart {
constructor(data) {
super(data);
this.type = 'line';
}
render() {
console.log('渲染折线图');
// 具体的渲染逻辑
}
}
class BarChart extends Chart {
constructor(data) {
super(data);
this.type = 'bar';
}
render() {
console.log('渲染柱状图');
// 具体的渲染逻辑
}
}
class PieChart extends Chart {
constructor(data) {
super(data);
this.type = 'pie';
}
render() {
console.log('渲染饼图');
// 具体的渲染逻辑
}
}
// ✅ 工厂类:封装对象创建逻辑
class ChartFactory {
static createChart(type, data, options = {}) {
let chart;
switch(type) {
case 'line':
chart = new LineChart(data);
// 封装复杂的初始化逻辑
chart.setAxisConfig(options.axis || { x: 'time', y: 'value' });
chart.enableTooltip();
break;
case 'bar':
chart = new BarChart(data);
chart.setColors(options.colors || ['#ff6384', '#36a2eb']);
chart.enableAnimation();
break;
case 'pie':
chart = new PieChart(data);
chart.enableLegend();
chart.setLabelFormat(options.labelFormat || 'percentage');
break;
default:
throw new Error(`不支持的图表类型: ${type}`);
}
return chart;
}
}
// ✅ 客户端代码:解耦,不直接依赖具体类
const lineChart = ChartFactory.createChart('line', salesData);
const barChart = ChartFactory.createChart('bar', revenueData, {
colors: ['#ff9999', '#66b3ff']
});
const pieChart = ChartFactory.createChart('pie', categoryData);
// 所有图表都可以统一处理
[lineChart, barChart, pieChart].forEach(chart => {
chart.render();
});
工厂方法模式
当需要更好的扩展性时,使用工厂方法模式:
javascript
// 抽象工厂基类
class ChartFactory {
createChart(data, options) {
throw new Error('子类必须实现 createChart 方法');
}
}
// 具体工厂
class LineChartFactory extends ChartFactory {
createChart(data, options = {}) {
const chart = new LineChart(data);
chart.setAxisConfig(options.axis || { x: 'time', y: 'value' });
chart.enableTooltip();
return chart;
}
}
class BarChartFactory extends ChartFactory {
createChart(data, options = {}) {
const chart = new BarChart(data);
chart.setColors(options.colors || ['#ff6384', '#36a2eb']);
chart.enableAnimation();
return chart;
}
}
// 工厂注册器 - 实现动态扩展
class ChartFactoryRegistry {
static factories = new Map();
static register(type, factory) {
this.factories.set(type, factory);
}
static createChart(type, data, options) {
const factory = this.factories.get(type);
if (!factory) {
throw new Error(`未注册的图表类型: ${type}`);
}
return factory.createChart(data, options);
}
static getSupportedTypes() {
return Array.from(this.factories.keys());
}
}
// 注册工厂
ChartFactoryRegistry.register('line', new LineChartFactory());
ChartFactoryRegistry.register('bar', new BarChartFactory());
// ✅ 扩展新类型:无需修改现有代码
class ScatterChart extends Chart {
constructor(data) {
super(data);
this.type = 'scatter';
}
render() {
console.log('渲染散点图');
}
}
class ScatterChartFactory extends ChartFactory {
createChart(data, options = {}) {
const chart = new ScatterChart(data);
chart.setPointSize(options.pointSize || 3);
return chart;
}
}
// 注册新工厂 - 完全不影响现有代码
ChartFactoryRegistry.register('scatter', new ScatterChartFactory());
// 使用
const charts = [
ChartFactoryRegistry.createChart('line', salesData),
ChartFactoryRegistry.createChart('bar', revenueData),
ChartFactoryRegistry.createChart('scatter', correlationData), // 新类型
];
console.log('支持的图表类型:', ChartFactoryRegistry.getSupportedTypes());
🌐 前端实际应用场景
1. HTTP 客户端工厂
不同的 API 需要不同配置的 HTTP 客户端:
javascript
class HttpClient {
constructor(config) {
this.baseURL = config.baseURL;
this.timeout = config.timeout;
this.headers = config.headers;
}
async get(url) {
// 模拟 HTTP GET 请求
console.log(`GET ${this.baseURL}${url}`);
return { data: 'mock data' };
}
}
class HttpClientFactory {
static createClient(type) {
const configs = {
api: {
baseURL: 'https://api.example.com',
timeout: 5000,
headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }
},
upload: {
baseURL: 'https://upload.example.com',
timeout: 30000,
headers: { 'Content-Type': 'multipart/form-data' }
},
analytics: {
baseURL: 'https://analytics.example.com',
timeout: 2000,
headers: { 'X-API-Key': process.env.ANALYTICS_API_KEY }
}
};
const config = configs[type];
if (!config) {
throw new Error(`不支持的客户端类型: ${type}`);
}
return new HttpClient(config);
}
}
// ✅ 使用:客户端代码完全解耦
const apiClient = HttpClientFactory.createClient('api');
const uploadClient = HttpClientFactory.createClient('upload');
apiClient.get('/users');
uploadClient.get('/files');
2. 表单验证器工厂
javascript
class EmailValidator {
validate(value) {
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
return { valid, message: valid ? '' : '请输入有效的邮箱地址' };
}
}
class PhoneValidator {
validate(value) {
const valid = /^1[3-9]\d{9}$/.test(value);
return { valid, message: valid ? '' : '请输入有效的手机号码' };
}
}
class ValidatorFactory {
static createValidator(type) {
const validators = {
email: () => new EmailValidator(),
phone: () => new PhoneValidator(),
};
const factory = validators[type];
if (!factory) {
throw new Error(`不支持的验证器类型: ${type}`);
}
return factory();
}
}
// ✅ 使用:统一的验证接口
const emailValidator = ValidatorFactory.createValidator('email');
const phoneValidator = ValidatorFactory.createValidator('phone');
console.log(emailValidator.validate('test@example.com'));
console.log(phoneValidator.validate('13800138000'));
3. React 组件工厂
jsx
// 基础组件
const Button = ({ variant, children, ...props }) => {
const classes = {
primary: 'bg-blue-500 text-white',
secondary: 'bg-gray-500 text-white'
};
return (
<button className={`px-4 py-2 rounded ${classes[variant]}`} {...props}>
{children}
</button>
);
};
const Input = ({ error, ...props }) => (
<div>
<input
className={`border rounded px-3 py-2 ${error ? 'border-red-500' : 'border-gray-300'}`}
{...props}
/>
{error && <p className="text-red-500 text-sm mt-1">{error}</p>}
</div>
);
// ✅ 组件工厂
class ComponentFactory {
static components = {
button: Button,
input: Input,
};
static createComponent(type, props) {
const Component = this.components[type];
if (!Component) {
throw new Error(`不支持的组件类型: ${type}`);
}
return React.createElement(Component, props);
}
}
// 使用
const MyForm = () => {
return (
<div>
{ComponentFactory.createComponent('input', {
placeholder: '请输入用户名'
})}
{ComponentFactory.createComponent('button', {
variant: 'primary',
children: '提交'
})}
</div>
);
};
🎯 工厂模式的核心价值
✅ 优势
- 解耦合:客户端不依赖具体产品类,只依赖抽象接口
- 易扩展:新增产品类型无需修改客户端代码
- 封装复杂性:隐藏对象创建的复杂逻辑
- 统一管理:集中处理对象创建,便于维护
- 符合开闭原则:对扩展开放,对修改封闭
⚠️ 注意事项
- 避免过度设计 :简单场景直接
new
可能更合适 - 性能考虑:工厂模式会增加一层抽象,但通常影响微乎其微
- 类型安全:在 TypeScript 中要注意工厂返回类型的定义
📋 何时使用工厂模式
- 需要根据条件创建不同类型的对象
- 对象创建过程复杂,需要封装
- 希望客户端代码不依赖具体的产品类
- 需要统一管理一类对象的创建
- 经常需要扩展新的产品类型
📝 总结
工厂设计模式的核心思想是 "不要直接 new,让工厂来创建"。它通过引入工厂这一中间层,实现了客户端代码与具体产品类的解耦,让代码更加灵活、易维护、易扩展。
在前端开发中,工厂模式特别适合用于:
- HTTP 客户端的创建和配置
- 表单验证器的统一管理
- UI 组件的动态生成
- 各种策略对象的创建
当你发现代码中到处都是 new
,且这些创建逻辑很相似时,就是考虑工厂模式的好时机。