您现在的位置是:网站首页 > 工厂模式(Factory Pattern)的实现与应用文章详情

工厂模式(Factory Pattern)的实现与应用

工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,使得客户端代码不需要直接依赖具体类。这种模式通过定义一个创建对象的接口,让子类决定实例化哪一个类,从而将对象的创建与使用分离。

工厂模式的基本概念

工厂模式的核心思想是将对象的创建过程抽象出来,形成一个独立的"工厂"角色。根据抽象程度的不同,工厂模式可以分为三种形式:

  1. 简单工厂模式(Simple Factory)
  2. 工厂方法模式(Factory Method)
  3. 抽象工厂模式(Abstract Factory)

在JavaScript中,由于语言的动态特性,工厂模式的实现相比静态类型语言更加灵活。我们可以利用函数、对象或类来实现不同形式的工厂模式。

简单工厂模式实现

简单工厂模式是最基础的工厂实现,它通过一个工厂类(或函数)来创建不同类型的对象,而不需要暴露具体的创建逻辑。

class Car {
  constructor(options) {
    this.type = options.type || '普通汽车';
    this.color = options.color || '白色';
  }
  
  drive() {
    console.log(`${this.color}的${this.type}正在行驶`);
  }
}

class CarFactory {
  static create(type, options) {
    switch(type) {
      case 'sports':
        return new Car({ ...options, type: '跑车' });
      case 'suv':
        return new Car({ ...options, type: 'SUV' });
      default:
        return new Car(options);
    }
  }
}

// 使用工厂创建汽车
const sportsCar = CarFactory.create('sports', { color: '红色' });
const suv = CarFactory.create('suv', { color: '黑色' });

sportsCar.drive(); // 红色的跑车正在行驶
suv.drive(); // 黑色的SUV正在行驶

这种模式的优点是客户端不需要知道具体产品的类名,只需要知道工厂类和参数即可。缺点是当需要添加新产品时,必须修改工厂类的逻辑,违反了开闭原则。

工厂方法模式实现

工厂方法模式是对简单工厂模式的进一步抽象,它定义了一个创建对象的接口,但让子类决定实例化哪个类。工厂方法让类的实例化推迟到子类。

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

// 具体工厂类 - 汽车工厂
class CarFactory extends VehicleFactory {
  createVehicle(options) {
    return new Car(options);
  }
}

// 具体工厂类 - 摩托车工厂
class MotorcycleFactory extends VehicleFactory {
  createVehicle(options) {
    return new Motorcycle(options);
  }
}

// 具体产品类 - 汽车
class Car {
  constructor(options) {
    this.type = '汽车';
    this.color = options.color;
  }
  
  drive() {
    console.log(`${this.color}的${this.type}正在行驶`);
  }
}

// 具体产品类 - 摩托车
class Motorcycle {
  constructor(options) {
    this.type = '摩托车';
    this.color = options.color;
  }
  
  ride() {
    console.log(`${this.color}的${this.type}正在骑行`);
  }
}

// 使用工厂
const carFactory = new CarFactory();
const myCar = carFactory.createVehicle({ color: '蓝色' });
myCar.drive();

const motorcycleFactory = new MotorcycleFactory();
const myMotorcycle = motorcycleFactory.createVehicle({ color: '黄色' });
myMotorcycle.ride();

工厂方法模式符合开闭原则,当需要添加新产品时,只需要添加新的工厂类即可,不需要修改现有代码。但是每增加一个产品类,就需要增加一个对应的工厂类,增加了系统的复杂度。

抽象工厂模式实现

抽象工厂模式是工厂方法模式的升级版,它用于创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。

// 抽象工厂接口
class UIFactory {
  createButton() {}
  createCheckbox() {}
}

// 具体工厂 - Material风格UI组件
class MaterialUIFactory extends UIFactory {
  createButton() {
    return new MaterialButton();
  }
  
  createCheckbox() {
    return new MaterialCheckbox();
  }
}

// 具体工厂 - Flat风格UI组件
class FlatUIFactory extends UIFactory {
  createButton() {
    return new FlatButton();
  }
  
  createCheckbox() {
    return new FlatCheckbox();
  }
}

// 具体产品 - Material按钮
class MaterialButton {
  render() {
    console.log('渲染Material风格的按钮');
  }
}

// 具体产品 - Material复选框
class MaterialCheckbox {
  render() {
    console.log('渲染Material风格的复选框');
  }
}

// 具体产品 - Flat按钮
class FlatButton {
  render() {
    console.log('渲染Flat风格的按钮');
  }
}

// 具体产品 - Flat复选框
class FlatCheckbox {
  render() {
    console.log('渲染Flat风格的复选框');
  }
}

// 客户端代码
function createUI(factory) {
  const button = factory.createButton();
  const checkbox = factory.createCheckbox();
  
  return { button, checkbox };
}

// 使用Material风格
const materialUI = createUI(new MaterialUIFactory());
materialUI.button.render();
materialUI.checkbox.render();

// 使用Flat风格
const flatUI = createUI(new FlatUIFactory());
flatUI.button.render();
flatUI.checkbox.render();

抽象工厂模式特别适合需要创建一系列相关产品的场景,它能确保客户端始终使用同一系列的产品。当需要增加新的产品系列时,只需要增加新的具体工厂类即可,符合开闭原则。

JavaScript中的工厂函数实现

在JavaScript中,我们还可以使用工厂函数来实现工厂模式,这种方式更加简洁灵活:

// 工厂函数实现
function createUser(type, options) {
  switch(type) {
    case 'admin':
      return {
        ...options,
        role: 'admin',
        permissions: ['create', 'read', 'update', 'delete'],
        greet() {
          console.log(`您好,管理员${this.name}`);
        }
      };
    case 'customer':
      return {
        ...options,
        role: 'customer',
        permissions: ['read'],
        greet() {
          console.log(`欢迎您,顾客${this.name}`);
        }
      };
    default:
      throw new Error('未知的用户类型');
  }
}

// 使用工厂函数
const admin = createUser('admin', { name: '张三' });
const customer = createUser('customer', { name: '李四' });

admin.greet(); // 您好,管理员张三
customer.greet(); // 欢迎您,顾客李四

工厂函数在JavaScript中非常常见,它利用了JavaScript的动态特性,可以灵活地创建和返回不同类型的对象。

工厂模式在前端框架中的应用

工厂模式在前端框架中有广泛的应用,例如React中的createElement就是一个典型的工厂方法:

// React.createElement的简化实现
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.length <= 1 ? children[0] : children
    }
  };
}

// 使用工厂方法创建React元素
const element = createElement('div', { className: 'container' }, 
  createElement('h1', null, 'Hello World'),
  createElement('p', null, 'This is a paragraph')
);

Vue中的组件系统也使用了工厂模式的思想,每个组件都可以看作是一个工厂,负责创建组件实例:

// Vue组件工厂的简化示例
function createComponent(options) {
  return {
    ...options,
    data() {
      return options.data ? options.data() : {};
    },
    mounted() {
      options.mounted && options.mounted.call(this);
    }
    // 其他生命周期和方法...
  };
}

// 使用组件工厂
const MyComponent = createComponent({
  data() {
    return { count: 0 };
  },
  methods: {
    increment() {
      this.count++;
    }
  },
  mounted() {
    console.log('组件已挂载');
  }
});

工厂模式与依赖注入

工厂模式经常与依赖注入(DI)一起使用,用于管理对象的创建和依赖关系:

// 依赖注入容器
class Container {
  constructor() {
    this.services = {};
    this.factories = {};
  }
  
  register(name, factory) {
    this.factories[name] = factory;
  }
  
  get(name) {
    if (!this.services[name]) {
      this.services[name] = this.factories[name](this);
    }
    return this.services[name];
  }
}

// 使用依赖注入容器
const container = new Container();

// 注册服务工厂
container.register('logger', () => {
  return {
    log: (message) => console.log(`[LOG]: ${message}`)
  };
});

container.register('userService', (c) => {
  const logger = c.get('logger');
  return {
    getUsers: () => {
      logger.log('获取用户列表');
      return ['张三', '李四'];
    }
  };
});

// 使用服务
const userService = container.get('userService');
console.log(userService.getUsers()); // [LOG]: 获取用户列表 \n ['张三', '李四']

这种模式在现代前端框架如Angular中非常常见,它使得代码更加模块化,便于测试和维护。

工厂模式的优缺点

优点

  1. 封装创建逻辑:将对象的创建过程集中管理,客户端代码不需要关心具体的创建细节
  2. 解耦:降低了客户端代码与具体产品类之间的耦合度
  3. 扩展性好:当需要添加新产品时,只需要扩展工厂类,不需要修改现有代码(工厂方法模式和抽象工厂模式)
  4. 统一管理:可以对对象的创建进行统一的管理和控制,如实现单例、缓存等

缺点

  1. 增加复杂度:每增加一个产品,就需要增加一个具体工厂类(工厂方法模式),增加了系统的复杂度
  2. 抽象性高:抽象工厂模式引入了许多接口和类,增加了系统的抽象性和理解难度
  3. 不适用于简单对象:对于简单的对象创建,使用工厂模式可能会显得过于复杂

工厂模式与其他设计模式的关系

工厂模式经常与其他设计模式一起使用或相互补充:

  1. 与单例模式:工厂可以返回单例对象,确保系统中某个类只有一个实例
  2. 与原型模式:工厂可以使用原型模式来克隆对象,而不是每次都创建新实例
  3. 与装饰器模式:工厂可以在返回对象前对其进行装饰,添加额外的功能
  4. 与策略模式:工厂可以根据不同的策略创建不同的对象

实际应用场景示例

场景一:跨平台UI组件

假设我们需要开发一个跨平台的UI库,支持Web和移动端:

// 抽象工厂
class UIComponentsFactory {
  createButton() {}
  createDialog() {}
}

// Web组件工厂
class WebComponentsFactory extends UIComponentsFactory {
  createButton() {
    return new WebButton();
  }
  
  createDialog() {
    return new WebDialog();
  }
}

// 移动端组件工厂
class MobileComponentsFactory extends UIComponentsFactory {
  createButton() {
    return new MobileButton();
  }
  
  createDialog() {
    return new MobileDialog();
  }
}

// 具体产品
class WebButton {
  render() {
    console.log('渲染Web风格的按钮');
  }
}

class WebDialog {
  render() {
    console.log('渲染Web风格的对话框');
  }
}

class MobileButton {
  render() {
    console.log('渲染移动端风格的按钮');
  }
}

class MobileDialog {
  render() {
    console.log('渲染移动端风格的对话框');
  }
}

// 客户端代码
function renderUI(factory) {
  const button = factory.createButton();
  const dialog = factory.createDialog();
  
  button.render();
  dialog.render();
}

// 根据平台选择工厂
const platform = 'web'; // 或'mobile'
const factory = platform === 'web' 
  ? new WebComponentsFactory() 
  : new MobileComponentsFactory();

renderUI(factory);

场景二:电商系统中的折扣策略

电商系统中不同的用户类型有不同的折扣策略:

// 折扣策略工厂
class DiscountStrategyFactory {
  static create(userType) {
    switch(userType) {
      case 'vip':
        return new VipDiscountStrategy();
      case 'regular':
        return new RegularDiscountStrategy();
      case 'new':
        return new NewCustomerDiscountStrategy();
      default:
        return new NoDiscountStrategy();
    }
  }
}

// 具体策略
class VipDiscountStrategy {
  calculate(price) {
    return price * 0.8; // 8折
  }
}

class RegularDiscountStrategy {
  calculate(price) {
    return price * 0.9; // 9折
  }
}

class NewCustomerDiscountStrategy {
  calculate(price) {
    return price * 0.95; // 95折
  }
}

class NoDiscountStrategy {
  calculate(price) {
    return price; // 无折扣
  }
}

// 使用工厂
const vipDiscount = DiscountStrategyFactory.create('vip');
console.log(vipDiscount.calculate(100)); // 80

const regularDiscount = DiscountStrategyFactory.create('regular');
console.log(regularDiscount.calculate(100)); // 90

场景三:游戏中的角色创建

游戏开发中,工厂模式常用于创建不同类型的游戏角色:

// 角色工厂
class CharacterFactory {
  static create(type, name) {
    switch(type) {
      case 'warrior':
        return new Warrior(name);
      case 'mage':
        return new Mage(name);
      case 'archer':
        return new Archer(name);
      default:
        throw new Error('未知的角色类型');
    }
  }
}

// 具体角色类
class Warrior {
  constructor(name) {
    this.name = name;
    this.health = 150;
    this.attack = 20;
  }
  
  attackEnemy() {
    console.log(`${this.name}用剑攻击,造成${this.attack}点伤害`);
  }
}

class Mage {
  constructor(name) {
    this.name = name;
    this.health = 80;
    this.attack = 30;
  }
  
  attackEnemy() {
    console.log(`${this.name}施放火球术,造成${this.attack}点伤害`);
  }
}

class Archer {
  constructor(name) {
    this.name = name;
    this.health = 100;
    this.attack = 25;
  }
  
  attackEnemy() {
    console.log(`${this.name}射箭,造成${this.attack}点伤害`);
  }
}

// 创建游戏角色
const hero1 = CharacterFactory.create('warrior', '亚瑟');
const hero2 = CharacterFactory.create('mage', '梅林');

hero1.attackEnemy(); // 亚瑟用剑攻击,造成20点伤害
hero2.attackEnemy(); // 梅林施放火球术,造成30点伤害

工厂模式的变体与扩展

延迟初始化工厂

工厂可以延迟对象的创建,直到真正需要时才创建实例:

class LazyFactory {
  constructor(creatorFn) {
    this.creatorFn = creatorFn;
    this.instance = null;
  }
  
  getInstance() {
    if (!this.instance) {
      this.instance = this.creatorFn();
    }
    return this.instance;
  }
}

// 使用延迟工厂
const heavyObjectFactory = new LazyFactory(() => {
  console.log('创建重量级对象...');
  return { data: '大量数据' };
});

// 第一次访问时才会创建对象
console.log(heavyObjectFactory.getInstance().data); // 创建重量级对象... \n 大量数据
// 第二次访问直接返回已有实例
console.log(heavyObjectFactory.getInstance().data); // 大量数据

多例工厂

工厂可以控制实例的数量,实现多例模式:

class MultiInstanceFactory {
  constructor(creatorFn, maxInstances = 3) {
    this.creatorFn = creatorFn;
    this.maxInstances = maxInstances;
    this.instances = [];
  }
  
  getInstance() {
    if (this.instances.length < this.maxInstances) {
      const newInstance = this.creatorFn();
      this.instances.push(newInstance);
      return newInstance;
    }
    
    // 循环使用已有实例
    const instance = this.instances.shift();
    this.instances.push(instance);
    return instance;
  }
}

// 使用多例工厂
const connectionFactory = new MultiInstanceFactory(() => {
  console.log('创建新的数据库连接');
  return { id: Math.random().toString(36).substr(2, 9) };
}, 2);

// 获取连接
const conn1 = connectionFactory.getInstance(); // 创建新的数据库连接
const conn2 = connectionFactory.getInstance(); // 创建新的数据库连接
const conn3 = connectionFactory.getInstance(); // 返回第一个连接

console.log(conn1 === conn3); // true

带缓存的工厂

工厂可以实现缓存机制,避免重复创建相同的对象:

class CachedFactory {
  constructor(creatorFn) {
    this.creatorFn = creatorFn;
    this.cache = new Map();
  }
  
  create(key, ...args) {
    if (this.cache.has(key)) {
      return this.cache.get(key);
    }
    
    const instance = this.creatorFn(...args);
    this.cache.set(key, instance);
    return instance;
  }
}

// 使用带缓存的工厂
const userFactory = new CachedFactory((id, name) => {
  console.log(`创建用户${name}`);
  return { id, name };
});

const user1 = userFactory.create(1, '张三'); // 创建用户张三
const user2 = userFactory.create(1, '张三'); // 从缓存获取

console.log(user1 === user2); // true

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步