Skip to content

工厂方法模式详解:概念、实现与应用

引言

工厂方法模式是创建型设计模式中的一个重要模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法让类的实例化推迟到子类。这种模式在现代软件开发中被广泛使用,特别是在需要创建多种相似对象的场景中。

什么是工厂方法模式?

工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

核心思想

工厂方法模式的核心思想是:

  1. 定义创建接口:定义一个创建对象的接口
  2. 延迟实例化:让子类决定实例化哪个具体类
  3. 解耦创建与使用:将对象的创建与使用分离

为什么需要工厂方法模式?

在许多情况下,我们需要根据不同的条件创建不同的对象:

1. 对象创建复杂

当对象的创建过程比较复杂时,使用工厂方法可以封装这些复杂性:

  • 需要进行大量的初始化工作
  • 需要根据不同的条件创建不同的对象
  • 需要管理对象的生命周期

2. 系统扩展性要求

当系统需要支持新的产品类型时,工厂方法模式可以很好地支持开闭原则:

  • 添加新的产品类不需要修改现有代码
  • 只需要添加新的具体工厂类

3. 隐藏对象创建细节

客户端不需要知道具体的产品类,只需要知道工厂接口:

  • 降低系统的耦合度
  • 提高代码的可维护性

工厂方法模式的基本实现

让我们从一个简单的工厂方法模式实现开始:

javascript
// 产品接口
class Product {
  operation() {
    throw new Error('必须实现 operation 方法');
  }
}

// 具体产品 A
class ConcreteProductA extends Product {
  operation() {
    return 'ConcreteProductA 的操作结果';
  }
}

// 具体产品 B
class ConcreteProductB extends Product {
  operation() {
    return 'ConcreteProductB 的操作结果';
  }
}

// 工厂接口
class Creator {
  // 工厂方法
  factoryMethod() {
    throw new Error('必须实现 factoryMethod 方法');
  }
  
  // 业务逻辑
  someOperation() {
    const product = this.factoryMethod();
    return `Creator: 同产品交互 ${product.operation()}`;
  }
}

// 具体工厂 A
class ConcreteCreatorA extends Creator {
  factoryMethod() {
    return new ConcreteProductA();
  }
}

// 具体工厂 B
class ConcreteCreatorB extends Creator {
  factoryMethod() {
    return new ConcreteProductB();
  }
}

// 使用示例
function clientCode(creator) {
  console.log(creator.someOperation());
}

console.log('App: 使用 ConcreteCreatorA.');
clientCode(new ConcreteCreatorA());

console.log('App: 使用 ConcreteCreatorB.');
clientCode(new ConcreteCreatorB());

实现要点分析

  1. 产品层次结构:定义产品接口和具体产品实现
  2. 工厂层次结构:定义工厂接口和具体工厂实现
  3. 延迟实例化:具体工厂决定创建哪个具体产品
  4. 解耦创建与使用:客户端只依赖工厂接口和产品接口

工厂方法模式的实际应用场景

1. UI 组件工厂

在前端开发中,工厂方法模式常用于创建不同平台的 UI 组件:

javascript
// UI 组件接口
class UIComponent {
  render() {
    throw new Error('必须实现 render 方法');
  }
}

// 按钮组件
class Button extends UIComponent {
  render() {
    return '<button>按钮</button>';
  }
}

// 输入框组件
class Input extends UIComponent {
  render() {
    return '<input type="text" />';
  }
}

// UI 工厂接口
class UIComponentFactory {
  createButton() {
    throw new Error('必须实现 createButton 方法');
  }
  
  createInput() {
    throw new Error('必须实现 createInput 方法');
  }
}

// Web UI 工厂
class WebUIFactory extends UIComponentFactory {
  createButton() {
    return new Button();
  }
  
  createInput() {
    return new Input();
  }
}

// 移动端 UI 工厂
class MobileUIFactory extends UIComponentFactory {
  createButton() {
    return new MobileButton();
  }
  
  createInput() {
    return new MobileInput();
  }
}

// 移动端特定组件
class MobileButton extends UIComponent {
  render() {
    return '<button class="mobile-button">移动端按钮</button>';
  }
}

class MobileInput extends UIComponent {
  render() {
    return '<input type="text" class="mobile-input" />';
  }
}

// 使用示例
function createUI(factory) {
  const button = factory.createButton();
  const input = factory.createInput();
  
  return {
    button: button.render(),
    input: input.render()
  };
}

// Web 端 UI
const webFactory = new WebUIFactory();
const webUI = createUI(webFactory);
console.log('Web UI:', webUI);

// 移动端 UI
const mobileFactory = new MobileUIFactory();
const mobileUI = createUI(mobileFactory);
console.log('Mobile UI:', mobileUI);

2. 数据库连接工厂

在后端开发中,工厂方法模式可以用于创建不同类型的数据库连接:

javascript
// 数据库连接接口
class DatabaseConnection {
  connect() {
    throw new Error('必须实现 connect 方法');
  }
  
  query(sql) {
    throw new Error('必须实现 query 方法');
  }
  
  close() {
    throw new Error('必须实现 close 方法');
  }
}

// MySQL 连接
class MySQLConnection extends DatabaseConnection {
  connect() {
    return '连接到 MySQL 数据库';
  }
  
  query(sql) {
    return `在 MySQL 中执行: ${sql}`;
  }
  
  close() {
    return '关闭 MySQL 连接';
  }
}

// PostgreSQL 连接
class PostgreSQLConnection extends DatabaseConnection {
  connect() {
    return '连接到 PostgreSQL 数据库';
  }
  
  query(sql) {
    return `在 PostgreSQL 中执行: ${sql}`;
  }
  
  close() {
    return '关闭 PostgreSQL 连接';
  }
}

// 数据库工厂接口
class DatabaseFactory {
  createConnection() {
    throw new Error('必须实现 createConnection 方法');
  }
}

// MySQL 工厂
class MySQLFactory extends DatabaseFactory {
  createConnection() {
    return new MySQLConnection();
  }
}

// PostgreSQL 工厂
class PostgreSQLFactory extends DatabaseFactory {
  createConnection() {
    return new PostgreSQLConnection();
  }
}

// 使用示例
class DatabaseManager {
  constructor(factory) {
    this.factory = factory;
    this.connection = null;
  }
  
  connect() {
    this.connection = this.factory.createConnection();
    return this.connection.connect();
  }
  
  executeQuery(sql) {
    if (!this.connection) {
      throw new Error('数据库未连接');
    }
    return this.connection.query(sql);
  }
  
  disconnect() {
    if (this.connection) {
      const result = this.connection.close();
      this.connection = null;
      return result;
    }
  }
}

// MySQL 使用示例
const mysqlFactory = new MySQLFactory();
const mysqlManager = new DatabaseManager(mysqlFactory);
console.log(mysqlManager.connect());
console.log(mysqlManager.executeQuery('SELECT * FROM users'));
console.log(mysqlManager.disconnect());

// PostgreSQL 使用示例
const postgresqlFactory = new PostgreSQLFactory();
const postgresqlManager = new DatabaseManager(postgresqlFactory);
console.log(postgresqlManager.connect());
console.log(postgresqlManager.executeQuery('SELECT * FROM users'));
console.log(postgresqlManager.disconnect());

3. 文件解析器工厂

在处理不同格式文件时,工厂方法模式也非常有用:

javascript
// 文件解析器接口
class FileParser {
  parse(content) {
    throw new Error('必须实现 parse 方法');
  }
}

// JSON 解析器
class JSONParser extends FileParser {
  parse(content) {
    try {
      return JSON.parse(content);
    } catch (error) {
      throw new Error(`JSON 解析失败: ${error.message}`);
    }
  }
}

// XML 解析器
class XMLParser extends FileParser {
  parse(content) {
    // 简化的 XML 解析示例
    const match = content.match(/<(\w+)>(.*?)<\/\1>/);
    if (match) {
      return { [match[1]]: match[2] };
    }
    throw new Error('XML 解析失败');
  }
}

// CSV 解析器
class CSVParser extends FileParser {
  parse(content) {
    const lines = content.trim().split('\n');
    const headers = lines[0].split(',').map(h => h.trim());
    const rows = [];
    
    for (let i = 1; i < lines.length; i++) {
      const values = lines[i].split(',').map(v => v.trim());
      const row = {};
      headers.forEach((header, index) => {
        row[header] = values[index];
      });
      rows.push(row);
    }
    
    return rows;
  }
}

// 解析器工厂接口
class ParserFactory {
  createParser(fileType) {
    throw new Error('必须实现 createParser 方法');
  }
}

// 具体解析器工厂
class ConcreteParserFactory extends ParserFactory {
  createParser(fileType) {
    switch (fileType.toLowerCase()) {
      case 'json':
        return new JSONParser();
      case 'xml':
        return new XMLParser();
      case 'csv':
        return new CSVParser();
      default:
        throw new Error(`不支持的文件类型: ${fileType}`);
    }
  }
}

// 使用示例
class FileProcessor {
  constructor(factory) {
    this.factory = factory;
  }
  
  processFile(content, fileType) {
    const parser = this.factory.createParser(fileType);
    try {
      const result = parser.parse(content);
      return {
        success: true,
        data: result
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

// 使用示例
const parserFactory = new ConcreteParserFactory();
const fileProcessor = new FileProcessor(parserFactory);

// JSON 处理
const jsonContent = '{"name": "张三", "age": 30}';
console.log('JSON 解析:', fileProcessor.processFile(jsonContent, 'json'));

// XML 处理
const xmlContent = '<user>张三</user>';
console.log('XML 解析:', fileProcessor.processFile(xmlContent, 'xml'));

// CSV 处理
const csvContent = 'name,age\n张三,30\n李四,25';
console.log('CSV 解析:', fileProcessor.processFile(csvContent, 'csv'));

工厂方法模式与简单工厂模式的对比

简单工厂模式

javascript
// 简单工厂
class SimpleFactory {
  static createProduct(type) {
    switch (type) {
      case 'A':
        return new ConcreteProductA();
      case 'B':
        return new ConcreteProductB();
      default:
        throw new Error(`不支持的产品类型: ${type}`);
    }
  }
}

// 使用简单工厂
const productA = SimpleFactory.createProduct('A');
const productB = SimpleFactory.createProduct('B');

工厂方法模式 vs 简单工厂模式

特性工厂方法模式简单工厂模式
扩展性非常好,符合开闭原则较差,添加新产品需要修改工厂类
复杂度较高,需要多个类较低,只需要一个工厂类
耦合度低,客户端依赖抽象中等,客户端依赖具体工厂
适用场景系统需要支持产品族扩展产品类型相对固定

工厂方法模式的优缺点

优点

  1. 符合开闭原则:添加新产品时无需修改现有代码
  2. 符合单一职责原则:每个工厂类只负责一种产品的创建
  3. 良好的封装性:客户端不需要知道具体产品的创建细节
  4. 良好的扩展性:可以轻松添加新的产品和工厂类

缺点

  1. 类数量增加:每增加一个产品类,就需要增加一个对应的工厂类
  2. 系统复杂度增加:引入了更多的抽象和继承关系
  3. 客户端理解成本:需要理解工厂类和产品类之间的关系

总结

工厂方法模式是创建型设计模式中的重要模式,它通过定义一个创建对象的接口,让子类决定实例化哪个类,从而实现了对象创建与使用的分离。

通过本章的学习,我们了解了:

  1. 工厂方法模式的基本概念和核心思想
  2. 工厂方法模式的实现方式
  3. 工厂方法模式在实际开发中的应用场景
  4. 工厂方法模式与简单工厂模式的对比
  5. 工厂方法模式的优缺点

工厂方法模式在现代软件开发中应用广泛,特别是在需要创建多种相似对象的场景中,它可以很好地支持系统的扩展性和维护性。

在下一章中,我们将继续探讨其他创建型模式,如抽象工厂模式。