跳到主要内容

工厂模式

问题

什么是工厂模式?简单工厂、工厂方法、抽象工厂有什么区别?前端有哪些实际应用?

答案

工厂模式是一种创建型设计模式,将对象的创建逻辑封装起来,通过统一接口创建对象,调用者无需关心具体实现。


核心概念

三种工厂模式对比

类型说明适用场景
简单工厂一个工厂创建所有产品产品类型少且固定
工厂方法每个产品对应一个工厂产品扩展频繁
抽象工厂创建产品家族多系列产品组合

简单工厂模式

基础实现

// 产品接口
interface Button {
render(): string;
onClick(handler: () => void): void;
}

// 具体产品
class PrimaryButton implements Button {
render() {
return '<button class="primary">Primary</button>';
}

onClick(handler: () => void) {
console.log('Primary button clicked');
handler();
}
}

class DangerButton implements Button {
render() {
return '<button class="danger">Danger</button>';
}

onClick(handler: () => void) {
console.log('Danger button clicked');
handler();
}
}

class LinkButton implements Button {
render() {
return '<button class="link">Link</button>';
}

onClick(handler: () => void) {
console.log('Link button clicked');
handler();
}
}

// 简单工厂
type ButtonType = 'primary' | 'danger' | 'link';

class ButtonFactory {
static create(type: ButtonType): Button {
switch (type) {
case 'primary':
return new PrimaryButton();
case 'danger':
return new DangerButton();
case 'link':
return new LinkButton();
default:
throw new Error(`Unknown button type: ${type}`);
}
}
}

// 使用
const primaryBtn = ButtonFactory.create('primary');
const dangerBtn = ButtonFactory.create('danger');
console.log(primaryBtn.render());

使用 Map 优化

// 避免 switch-case,使用 Map
const buttonMap = new Map<ButtonType, new () => Button>([
['primary', PrimaryButton],
['danger', DangerButton],
['link', LinkButton],
]);

class ButtonFactoryV2 {
static create(type: ButtonType): Button {
const ButtonClass = buttonMap.get(type);
if (!ButtonClass) {
throw new Error(`Unknown button type: ${type}`);
}
return new ButtonClass();
}

// 支持注册新类型
static register(type: ButtonType, ButtonClass: new () => Button) {
buttonMap.set(type, ButtonClass);
}
}

工厂方法模式

// 产品接口
interface Dialog {
title: string;
render(): string;
}

// 工厂接口
interface DialogFactory {
createDialog(): Dialog;
}

// 具体产品
class ConfirmDialog implements Dialog {
title = '确认';

render() {
return `
<div class="dialog confirm">
<h2>${this.title}</h2>
<button>确认</button>
<button>取消</button>
</div>
`;
}
}

class AlertDialog implements Dialog {
title = '警告';

render() {
return `
<div class="dialog alert">
<h2>${this.title}</h2>
<button>知道了</button>
</div>
`;
}
}

// 具体工厂
class ConfirmDialogFactory implements DialogFactory {
createDialog() {
return new ConfirmDialog();
}
}

class AlertDialogFactory implements DialogFactory {
createDialog() {
return new AlertDialog();
}
}

// 使用
function showDialog(factory: DialogFactory) {
const dialog = factory.createDialog();
console.log(dialog.render());
}

showDialog(new ConfirmDialogFactory());
showDialog(new AlertDialogFactory());

抽象工厂模式

// 产品接口
interface Input {
render(): string;
getValue(): string;
}

interface Select {
render(): string;
getSelected(): string;
}

interface Checkbox {
render(): string;
isChecked(): boolean;
}

// 抽象工厂
interface UIFactory {
createInput(): Input;
createSelect(): Select;
createCheckbox(): Checkbox;
}

// Ant Design 风格产品
class AntInput implements Input {
render() {
return '<input class="ant-input" />';
}
getValue() {
return 'ant-input-value';
}
}

class AntSelect implements Select {
render() {
return '<select class="ant-select"></select>';
}
getSelected() {
return 'ant-selected';
}
}

class AntCheckbox implements Checkbox {
render() {
return '<input type="checkbox" class="ant-checkbox" />';
}
isChecked() {
return false;
}
}

// Element UI 风格产品
class ElementInput implements Input {
render() {
return '<input class="el-input" />';
}
getValue() {
return 'el-input-value';
}
}

class ElementSelect implements Select {
render() {
return '<select class="el-select"></select>';
}
getSelected() {
return 'el-selected';
}
}

class ElementCheckbox implements Checkbox {
render() {
return '<input type="checkbox" class="el-checkbox" />';
}
isChecked() {
return false;
}
}

// 具体工厂
class AntDesignFactory implements UIFactory {
createInput() {
return new AntInput();
}
createSelect() {
return new AntSelect();
}
createCheckbox() {
return new AntCheckbox();
}
}

class ElementUIFactory implements UIFactory {
createInput() {
return new ElementInput();
}
createSelect() {
return new ElementSelect();
}
createCheckbox() {
return new ElementCheckbox();
}
}

// 使用 - 切换 UI 框架只需更换工厂
function renderForm(factory: UIFactory) {
const input = factory.createInput();
const select = factory.createSelect();
const checkbox = factory.createCheckbox();

return `
<form>
${input.render()}
${select.render()}
${checkbox.render()}
</form>
`;
}

// 使用 Ant Design
console.log(renderForm(new AntDesignFactory()));
// 使用 Element UI
console.log(renderForm(new ElementUIFactory()));

前端实际应用

1. 组件工厂

import React, { ComponentType } from 'react';

interface FieldProps {
name: string;
label: string;
value: unknown;
onChange: (value: unknown) => void;
}

// 表单字段组件
const TextField: React.FC<FieldProps> = ({ name, label, value, onChange }) => (
<div>
<label>{label}</label>
<input
name={name}
value={value as string}
onChange={(e) => onChange(e.target.value)}
/>
</div>
);

const SelectField: React.FC<FieldProps & { options: string[] }> = ({
name,
label,
value,
onChange,
options,
}) => (
<div>
<label>{label}</label>
<select
name={name}
value={value as string}
onChange={(e) => onChange(e.target.value)}
>
{options.map((opt) => (
<option key={opt} value={opt}>
{opt}
</option>
))}
</select>
</div>
);

const CheckboxField: React.FC<FieldProps> = ({ name, label, value, onChange }) => (
<div>
<label>
<input
type="checkbox"
name={name}
checked={value as boolean}
onChange={(e) => onChange(e.target.checked)}
/>
{label}
</label>
</div>
);

// 字段工厂
type FieldType = 'text' | 'select' | 'checkbox';

const fieldComponents: Record<FieldType, ComponentType<FieldProps & Record<string, unknown>>> = {
text: TextField,
select: SelectField,
checkbox: CheckboxField,
};

function createField(type: FieldType, props: FieldProps & Record<string, unknown>) {
const Component = fieldComponents[type];
if (!Component) {
throw new Error(`Unknown field type: ${type}`);
}
return <Component {...props} />;
}

// 使用
function DynamicForm() {
const fields = [
{ type: 'text' as const, name: 'username', label: '用户名' },
{ type: 'select' as const, name: 'role', label: '角色', options: ['admin', 'user'] },
{ type: 'checkbox' as const, name: 'remember', label: '记住我' },
];

return (
<form>
{fields.map((field) =>
createField(field.type, {
...field,
value: '',
onChange: (v) => console.log(v),
})
)}
</form>
);
}

2. 图表工厂

interface Chart {
data: unknown[];
render(container: HTMLElement): void;
update(data: unknown[]): void;
destroy(): void;
}

class LineChart implements Chart {
data: unknown[] = [];

render(container: HTMLElement) {
console.log('Rendering line chart in', container);
}

update(data: unknown[]) {
this.data = data;
console.log('Updating line chart');
}

destroy() {
console.log('Destroying line chart');
}
}

class BarChart implements Chart {
data: unknown[] = [];

render(container: HTMLElement) {
console.log('Rendering bar chart in', container);
}

update(data: unknown[]) {
this.data = data;
console.log('Updating bar chart');
}

destroy() {
console.log('Destroying bar chart');
}
}

class PieChart implements Chart {
data: unknown[] = [];

render(container: HTMLElement) {
console.log('Rendering pie chart in', container);
}

update(data: unknown[]) {
this.data = data;
console.log('Updating pie chart');
}

destroy() {
console.log('Destroying pie chart');
}
}

// 图表工厂
type ChartType = 'line' | 'bar' | 'pie';

class ChartFactory {
private static chartMap = new Map<ChartType, new () => Chart>([
['line', LineChart],
['bar', BarChart],
['pie', PieChart],
]);

static create(type: ChartType): Chart {
const ChartClass = this.chartMap.get(type);
if (!ChartClass) {
throw new Error(`Unknown chart type: ${type}`);
}
return new ChartClass();
}

static register(type: ChartType, ChartClass: new () => Chart) {
this.chartMap.set(type, ChartClass);
}
}

// 使用
const chart = ChartFactory.create('line');
chart.render(document.getElementById('chart')!);

3. HTTP 请求适配器工厂

interface HttpAdapter {
get<T>(url: string): Promise<T>;
post<T>(url: string, data: unknown): Promise<T>;
}

class FetchAdapter implements HttpAdapter {
async get<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json();
}

async post<T>(url: string, data: unknown): Promise<T> {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return response.json();
}
}

class AxiosAdapter implements HttpAdapter {
async get<T>(url: string): Promise<T> {
// 模拟 axios
const response = await fetch(url);
return response.json();
}

async post<T>(url: string, data: unknown): Promise<T> {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
return response.json();
}
}

// 工厂
type AdapterType = 'fetch' | 'axios';

class HttpAdapterFactory {
static create(type: AdapterType): HttpAdapter {
switch (type) {
case 'fetch':
return new FetchAdapter();
case 'axios':
return new AxiosAdapter();
default:
return new FetchAdapter();
}
}
}

// 使用
const http = HttpAdapterFactory.create('fetch');
const data = await http.get('/api/users');

4. 消息通知工厂

interface Notification {
show(message: string): void;
hide(): void;
}

class ToastNotification implements Notification {
private element: HTMLDivElement | null = null;

show(message: string) {
this.element = document.createElement('div');
this.element.className = 'toast';
this.element.textContent = message;
document.body.appendChild(this.element);

setTimeout(() => this.hide(), 3000);
}

hide() {
this.element?.remove();
}
}

class ModalNotification implements Notification {
private element: HTMLDivElement | null = null;

show(message: string) {
this.element = document.createElement('div');
this.element.className = 'modal-notification';
this.element.innerHTML = `
<div class="modal-content">
<p>${message}</p>
<button onclick="this.parentElement.parentElement.remove()">关闭</button>
</div>
`;
document.body.appendChild(this.element);
}

hide() {
this.element?.remove();
}
}

class BannerNotification implements Notification {
private element: HTMLDivElement | null = null;

show(message: string) {
this.element = document.createElement('div');
this.element.className = 'banner';
this.element.textContent = message;
document.body.prepend(this.element);
}

hide() {
this.element?.remove();
}
}

// 工厂
type NotificationType = 'toast' | 'modal' | 'banner';

function createNotification(type: NotificationType): Notification {
const notifications: Record<NotificationType, new () => Notification> = {
toast: ToastNotification,
modal: ModalNotification,
banner: BannerNotification,
};

return new notifications[type]();
}

// 使用
const notification = createNotification('toast');
notification.show('操作成功!');

常见面试问题

Q1: 简单工厂、工厂方法、抽象工厂的区别?

答案

// 1. 简单工厂 - 一个工厂创建所有产品
class SimpleFactory {
static create(type: string) {
if (type === 'A') return new ProductA();
if (type === 'B') return new ProductB();
}
}

// 2. 工厂方法 - 每个产品对应工厂
interface Factory {
create(): Product;
}
class ProductAFactory implements Factory {
create() {
return new ProductA();
}
}

// 3. 抽象工厂 - 创建产品家族
interface UIFactory {
createButton(): Button;
createInput(): Input;
}
class MacUIFactory implements UIFactory {
createButton() {
return new MacButton();
}
createInput() {
return new MacInput();
}
}
对比项简单工厂工厂方法抽象工厂
复杂度
扩展性需修改工厂新增工厂类新增工厂类
产品关系单一产品单一产品产品家族
适用场景产品类型少产品扩展多多系列产品

Q2: 工厂模式的优缺点?

答案

优点缺点
解耦创建和使用增加类的数量
易扩展新产品简单工厂违反开闭原则
符合单一职责增加系统抽象性
隐藏实现细节-

Q3: 什么时候使用工厂模式?

答案

// 场景 1:对象创建逻辑复杂
class ComplexObject {
constructor() {
// 需要大量初始化
}
}
const factory = {
create: () => {
const obj = new ComplexObject();
// 复杂初始化...
return obj;
},
};

// 场景 2:根据配置创建不同对象
const componentFactory = (config: { type: string }) => {
switch (config.type) {
case 'button':
return new Button();
case 'input':
return new Input();
}
};

// 场景 3:需要统一管理对象创建(如池化、缓存)
class ConnectionFactory {
private pool: Connection[] = [];

create() {
if (this.pool.length > 0) {
return this.pool.pop()!;
}
return new Connection();
}

release(conn: Connection) {
this.pool.push(conn);
}
}

Q4: React 中的工厂模式应用?

答案

// React.createElement 就是工厂函数
const element = React.createElement('div', { className: 'box' }, 'Hello');

// 高阶组件也是工厂模式
function withLoading<P extends object>(Component: React.ComponentType<P>) {
return function WithLoadingComponent(props: P & { loading: boolean }) {
const { loading, ...rest } = props;
if (loading) return <div>Loading...</div>;
return <Component {...(rest as P)} />;
};
}

// 动态组件渲染
const componentMap = {
text: TextInput,
select: SelectInput,
checkbox: CheckboxInput,
};

function renderField(type: keyof typeof componentMap, props: FieldProps) {
const Component = componentMap[type];
return <Component {...props} />;
}

相关链接