工厂模式
在日常开发中,我们经常需要根据不同条件创建不同的对象。最直接的方式是在代码中到处使用 new,但这样会带来严重的耦合问题。工厂模式正是为了解决这个问题而生的:它让代码不再直接依赖具体类,而是通过“工厂”来创建对象,从而实现解耦。
工厂模式的核心思想是:不要直接 new,让工厂来创建。通过引入工厂这一中间层,实现客户端代码与具体产品类的解耦,让代码更加灵活、易维护、易扩展。
在前端开发中,它尤其适合用于:
- HTTP 客户端的创建与配置
- 表单验证器的统一管理
- UI 组件的动态生成
- 各类策略对象、解析器、适配器的生产与注册
当你发现代码中到处都是 new,且创建逻辑很相似或频繁改动时,就是考虑工厂模式的好时机。
它通常带来这些收益:
- 解耦对象创建:客户端不直接依赖具体类
- 封装创建逻辑:复杂初始化过程隐藏在工厂内部
- 统一创建入口:提供一致的对象获取方式
- 易于扩展:新增产品类型时尽量不改客户端代码
1. 为什么需要工厂模式?
1.1 直接使用 new 的痛点
假设你在做数据可视化应用,需要根据用户选择创建不同类型的图表:
// ❌ 糟糕的做法:到处都是 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 - 违反开闭原则:应该“对扩展开放,对修改封闭”,但这里每次扩展都在改代码
---
2. 简单工厂模式(Simple Factory)
适用于:产品类型相对固定、创建逻辑集中管理的场景。
2.1 产品抽象与具体产品
// 产品基类(抽象概念)
class Chart {
constructor(data) {
this.data = data;
}
render() {
throw new Error("子类必须实现 render 方法");
}
}
class LineChart extends Chart {
constructor(data) {
super(data);
this.type = "line";
}
setAxisConfig() {}
enableTooltip() {}
render() {
console.log("渲染折线图");
}
}
class BarChart extends Chart {
constructor(data) {
super(data);
this.type = "bar";
}
setColors() {}
enableAnimation() {}
render() {
console.log("渲染柱状图");
}
}
class PieChart extends Chart {
constructor(data) {
super(data);
this.type = "pie";
}
enableLegend() {}
setLabelFormat() {}
render() {
console.log("渲染饼图");
}
}2.2 工厂:封装创建逻辑
// ✅ 工厂:把“选择哪种类 + 如何初始化”的逻辑集中起来
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());简单工厂的优点是:调用方更干净。缺点是:如果产品类型不断增加,工厂内部的 switch/case 会膨胀,仍需要修改工厂代码。
4. 工厂方法模式(Factory Method)
适用于:希望更强扩展性、让“创建逻辑随着类型扩展而扩展”,并尽量避免改动原有工厂代码的场景。
核心思路:把“创建哪种产品”的决定,交给不同的具体工厂类。
4.1 抽象工厂与具体工厂
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;
}
}4.2 工厂注册器:实现动态扩展
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";
}
setPointSize() {}
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());4. 前端实际应用场景
4.1 HTTP 客户端工厂
不同 API 需要不同配置的 HTTP Client(baseURL、超时、header 等):
class HttpClient {
constructor(config) {
this.baseURL = config.baseURL;
this.timeout = config.timeout;
this.headers = config.headers;
}
async get(url) {
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");4.2 表单验证器工厂
统一校验接口,按类型生产不同校验器:
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"));4.3 React 组件工厂(动态生成组件)
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>
);
};