← 返回导航

工厂模式

设计模式 · 实战

场景说明

工厂模式把对象的创建过程封装起来,调用方只需告诉工厂"要什么",不需要知道具体怎么 new。前端常见于:根据接口字段类型创建不同形状的数据模型、根据按钮类型创建不同样式组件、根据支付渠道创建不同支付处理器。本 demo 用 TypeScript 实现类型映射驱动的工厂:一个注册表把"类型标识"映射到"构造器",工厂函数根据标识返回对应实例,且返回类型自动收窄为对应子类。

完整代码


      
// 工厂模式:类型映射驱动的对象创建
// 根据标识创建对应类型实例,返回类型自动收窄

// 1. 形状基类与子类
abstract class Shape {
  abstract area(): number;
  abstract describe(): string;
}

class Circle extends Shape {
  constructor(public radius: number) { super(); }
  area() { return Math.PI * this.radius ** 2; }
  describe() { return '圆形 r=' + this.radius; }
}

class Rectangle extends Shape {
  constructor(public w: number, public h: number) { super(); }
  area() { return this.w * this.h; }
  describe() { return '矩形 ' + this.w + 'x' + this.h; }
}

class Triangle extends Shape {
  constructor(public base: number, public height: number) { super(); }
  area() { return 0.5 * this.base * this.height; }
  describe() { return '三角形底' + this.base + '高' + this.height; }
}

// 2. 类型映射表:标识 => 构造器(每种子类对应一组参数类型)
interface ShapeRegistry {
  circle: typeof Circle;
  rectangle: typeof Rectangle;
  triangle: typeof Triangle;
}

// 3. 提取构造器参数元组类型
type CtorArgs<C> = C extends new (...args: infer A) => unknown ? A : never;

// 4. 实例类型
type Instance<C> = C extends new (...args: any[]) => infer I ? I : never;

// 5. 工厂函数:标识与参数元组强绑定,返回精确实例类型
function createShape<K extends keyof ShapeRegistry>(
  type: K,
  ...args: CtorArgs<ShapeRegistry[K]>
): Instance<ShapeRegistry[K]> {
  const registry: ShapeRegistry = {
    circle: Circle,
    rectangle: Rectangle,
    triangle: Triangle,
  };
  const Ctor = registry[type];
  return new Ctor(...args);
}

// 应用:参数类型与标识一一对应
const c = createShape('circle', 5);          // Circle,参数 [number]
const r = createShape('rectangle', 4, 6);  // Rectangle,参数 [number, number]
const t = createShape('triangle', 3, 8);   // Triangle,参数 [number, number]

console.log(c.describe(), c.area().toFixed(2));
console.log(r.describe(), r.area());
console.log(t.describe(), t.area());

关键点解析

使用示例与类型推导


      
// 参数个数错误会被拦截
// createShape('circle', 5, 6);
// 报错:Circle 构造器只接收 1 个参数

// 标识拼错会被拦截
// createShape('circl', 5);
// 报错:circl 不在 keyof ShapeRegistry 中

// 返回类型精确收窄,可访问子类特有属性
const circle = createShape('circle', 10);
console.log(circle.radius); // 10,Circle 特有属性可访问

const rect = createShape('rectangle', 3, 4);
console.log(rect.w, rect.h); // 3 4,Rectangle 特有属性可访问

// 批量创建:用映射类型生成联合
type ShapeKind = keyof ShapeRegistry; // 'circle' | 'rectangle' | 'triangle'

// 配置驱动创建:常见于接口返回 type 字段
function fromConfig(cfg: { type: ShapeKind; args: number[] }) {
  return createShape(cfg.type, ...(cfg.args as [number, number])); // 运行时灵活,类型需断言
}
在 Playground 中尝试