Factory Pattern

JavaScript 工厂模式的实现与应用

问题

实现工厂模式,包括简单工厂、工厂方法和抽象工厂三种形式。

解答

简单工厂

// 产品类
class Car {
  constructor(name) {
    this.name = name;
  }
  drive() {
    console.log(`${this.name} is driving`);
  }
}

class Bike {
  constructor(name) {
    this.name = name;
  }
  ride() {
    console.log(`${this.name} is riding`);
  }
}

// 简单工厂:根据类型创建不同产品
class VehicleFactory {
  static create(type, name) {
    switch (type) {
      case 'car':
        return new Car(name);
      case 'bike':
        return new Bike(name);
      default:
        throw new Error(`Unknown vehicle type: ${type}`);
    }
  }
}

// 使用
const car = VehicleFactory.create('car', 'Tesla');
const bike = VehicleFactory.create('bike', 'Giant');
car.drive();  // Tesla is driving
bike.ride();  // Giant is riding

工厂方法

// 抽象工厂类
class VehicleFactory {
  // 工厂方法,由子类实现
  createVehicle() {
    throw new Error('Must implement createVehicle');
  }

  // 业务逻辑
  deliver() {
    const vehicle = this.createVehicle();
    vehicle.prepare();
    vehicle.ship();
    return vehicle;
  }
}

// 具体产品
class Car {
  prepare() {
    console.log('Preparing car...');
  }
  ship() {
    console.log('Shipping car by truck');
  }
}

class Ship {
  prepare() {
    console.log('Preparing ship...');
  }
  ship() {
    console.log('Ship sails away');
  }
}

// 具体工厂
class CarFactory extends VehicleFactory {
  createVehicle() {
    return new Car();
  }
}

class ShipFactory extends VehicleFactory {
  createVehicle() {
    return new Ship();
  }
}

// 使用:客户端只依赖抽象工厂
function orderVehicle(factory) {
  return factory.deliver();
}

orderVehicle(new CarFactory());
orderVehicle(new ShipFactory());

抽象工厂

// 抽象产品
class Button {
  render() {}
}

class Input {
  render() {}
}

// Windows 风格产品
class WindowsButton extends Button {
  render() {
    return '<button class="win-btn">Windows Button</button>';
  }
}

class WindowsInput extends Input {
  render() {
    return '<input class="win-input" />';
  }
}

// Mac 风格产品
class MacButton extends Button {
  render() {
    return '<button class="mac-btn">Mac Button</button>';
  }
}

class MacInput extends Input {
  render() {
    return '<input class="mac-input" />';
  }
}

// 抽象工厂:创建一系列相关产品
class UIFactory {
  createButton() {}
  createInput() {}
}

// 具体工厂
class WindowsUIFactory extends UIFactory {
  createButton() {
    return new WindowsButton();
  }
  createInput() {
    return new WindowsInput();
  }
}

class MacUIFactory extends UIFactory {
  createButton() {
    return new MacButton();
  }
  createInput() {
    return new MacInput();
  }
}

// 使用:根据平台选择工厂
function createUI(factory) {
  const button = factory.createButton();
  const input = factory.createInput();
  return {
    button: button.render(),
    input: input.render()
  };
}

const platform = navigator.platform.includes('Mac') ? 'mac' : 'windows';
const factory = platform === 'mac' ? new MacUIFactory() : new WindowsUIFactory();
console.log(createUI(factory));

实际应用:React 组件工厂

// 根据配置动态创建表单组件
const componentMap = {
  text: ({ name, label }) => (
    <input type="text" name={name} placeholder={label} />
  ),
  select: ({ name, label, options }) => (
    <select name={name}>
      {options.map(opt => <option key={opt.value} value={opt.value}>{opt.label}</option>)}
    </select>
  ),
  checkbox: ({ name, label }) => (
    <label><input type="checkbox" name={name} /> {label}</label>
  )
};

// 组件工厂
function createFormField(config) {
  const Component = componentMap[config.type];
  if (!Component) {
    throw new Error(`Unknown field type: ${config.type}`);
  }
  return <Component key={config.name} {...config} />;
}

// 使用
const formConfig = [
  { type: 'text', name: 'username', label: 'Username' },
  { type: 'select', name: 'role', label: 'Role', options: [
    { value: 'admin', label: 'Admin' },
    { value: 'user', label: 'User' }
  ]},
  { type: 'checkbox', name: 'agree', label: 'I agree' }
];

function DynamicForm({ config }) {
  return <form>{config.map(createFormField)}</form>;
}

关键点

  • 简单工厂:一个工厂类,通过参数决定创建哪种产品,适合产品种类少的场景
  • 工厂方法:定义创建对象的接口,让子类决定实例化哪个类,符合开闭原则
  • 抽象工厂:创建一系列相关产品,保证产品族的一致性
  • 使用场景:对象创建逻辑复杂、需要解耦创建和使用、需要统一管理对象创建
  • 前端应用:动态组件渲染、主题切换、跨平台适配