TypeScript设计模式:工厂方法模式

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,用于定义一个创建对象的接口,但将实际的实例化过程延迟到子类中实现。这允许子类决定实例化哪种具体类,从而实现灵活的对象创建。

什么是工厂方法模式?

工厂方法模式的核心是通过抽象工厂类定义一个创建对象的方法(工厂方法),由子类实现具体的对象创建逻辑。这样可以实现:

  • 解耦:客户端代码与具体类的实例化过程分离,只依赖抽象接口。
  • 扩展性:通过添加新的子类,可以轻松支持新的产品类型。
  • 单一职责:将对象创建逻辑从业务逻辑中分离,增强代码清晰度。

在 TypeScript 中,工厂方法模式通过接口和抽象类结合类型系统,确保创建的对象符合预期类型。

工厂方法模式的结构

工厂方法模式通常包含以下几个角色:

  1. Product(产品接口):定义工厂方法创建的对象的接口。
  2. ConcreteProduct(具体产品):实现产品接口,定义具体的对象。
  3. Creator(创建者接口/抽象类):声明工厂方法,返回 Product 类型。
  4. ConcreteCreator(具体创建者):实现工厂方法,创建具体产品实例。

优点

  • 解耦合:客户端无需知道具体类的实现,只通过接口交互。
  • 扩展性:添加新产品只需实现新产品类和对应的工厂类。
  • 类型安全:TypeScript 的类型系统确保工厂方法返回正确的对象类型。
  • 单一职责:创建逻辑与使用逻辑分离,符合设计原则。

适用场景

  • 当对象的创建过程复杂或需要根据条件选择具体类型时。
  • 需要支持多种类似对象,且可能在未来扩展新类型时(如文档解析器、UI 组件生成器)。
  • 希望将对象的创建与使用分离,提升代码可维护性。

简单的模拟代码介绍

以下是一个简单的 TypeScript 示例,模拟一个文档生成器的工厂方法模式。我们定义一个文档接口和工厂类,不同类型的文档(如 PDF 和 Word)通过具体工厂创建。

typescript 复制代码
// 产品接口
interface Document {
  generate(): string;
}

// 具体产品:PDF 文档
class PDFDocument implements Document {
  generate(): string {
    return "Generating PDF document";
  }
}

// 具体产品:Word 文档
class WordDocument implements Document {
  generate(): string {
    return "Generating Word document";
  }
}

// 创建者抽象类
abstract class DocumentCreator {
  // 工厂方法
  abstract createDocument(): Document;

  // 使用工厂方法的操作
  generateDocument(): string {
    const document = this.createDocument();
    return document.generate();
  }
}

// 具体创建者:PDF 文档工厂
class PDFDocumentCreator extends DocumentCreator {
  createDocument(): Document {
    return new PDFDocument();
  }
}

// 具体创建者:Word 文档工厂
class WordDocumentCreator extends DocumentCreator {
  createDocument(): Document {
    return new WordDocument();
  }
}

// 客户端代码
function main() {
  const pdfCreator = new PDFDocumentCreator();
  console.log("Creating PDF:");
  console.log(pdfCreator.generateDocument());

  console.log("\nCreating Word:");
  const wordCreator = new WordDocumentCreator();
  console.log(wordCreator.generateDocument());
}

main();

运行结果

运行以上代码,将输出:

javascript 复制代码
Creating PDF:
Generating PDF document

Creating Word:
Generating Word document

这个模拟示例展示了工厂方法模式的核心:通过抽象工厂方法(createDocument)和具体工厂类(PDFDocumentCreatorWordDocumentCreator)分离创建逻辑,支持不同文档类型的生成。

AI知识库文件解析的实际应用示例

在 AI 知识库管理中,工厂方法模式常用于根据文件类型动态创建适当的解析器,以处理不同格式的文件(如 Markdown、PDF、Word)。客户端只需调用工厂方法,无需了解具体解析器的实现。以下是一个 Node.js 示例,使用 TypeScript 实现一个文件解析器工厂,支持解析 Markdown (.md)、PDF (.pdf) 和 Word (.docx) 文件,提取结构化内容(如标题和段落)并输出为 JSON。我们使用外部库(marked for Markdown、pdf-parse for PDF、mammoth for Word),需安装(npm install marked pdf-parse mammoth)。

实现示例

定义统一的内容模型和解析器接口,工厂方法根据文件扩展名创建对应的解析器。

typescript 复制代码
import * as fs from 'fs/promises';
import * as path from 'path';
import { marked } from 'marked'; // 用于 Markdown 解析
import pdfParse from 'pdf-parse'; // 用于 PDF 解析
import mammoth from 'mammoth'; // 用于 Word 解析

// 定义统一的内容模型
interface ContentSection {
  title: string;
  paragraph: string;
}

// 产品接口:文件解析器
interface FileParser {
  parse(filePath: string): Promise<ContentSection[]>;
}

// 具体产品:Markdown 解析器
class MarkdownFileParser implements FileParser {
  async parse(filePath: string): Promise<ContentSection[]> {
    console.log(`Parsing Markdown file: ${filePath}`);
    const content = await fs.readFile(filePath, 'utf-8');
    const tokens = marked.lexer(content);
    const sections: ContentSection[] = [];
    let currentTitle = '';
    tokens.forEach(token => {
      if (token.type === 'heading') {
        currentTitle = token.text;
      } else if (token.type === 'paragraph' && currentTitle) {
        sections.push({ title: currentTitle, paragraph: token.text });
        currentTitle = '';
      }
    });
    return sections;
  }
}

// 具体产品:PDF 解析器
class PDFFileParser implements FileParser {
  async parse(filePath: string): Promise<ContentSection[]> {
    console.log(`Parsing PDF file: ${filePath}`);
    const buffer = await fs.readFile(filePath);
    const data = await pdfParse(buffer);
    const lines = data.text.split('\n').filter(line => line.trim());
    const sections: ContentSection[] = [];
    for (let i = 0; i < lines.length; i += 2) {
      sections.push({
        title: lines[i] || 'Untitled',
        paragraph: lines[i + 1] || ''
      });
    }
    return sections;
  }
}

// 具体产品:Word 解析器
class WordFileParser implements FileParser {
  async parse(filePath: string): Promise<ContentSection[]> {
    console.log(`Parsing Word file: ${filePath}`);
    const buffer = await fs.readFile(filePath);
    const result = await mammoth.extractRawText({ buffer });
    const paragraphs = result.value.split('\n\n').filter(p => p.trim());
    return paragraphs.map((p, index) => ({
      title: `Section ${index + 1}`,
      paragraph: p
    }));
  }
}

// 创建者抽象类
abstract class ParserCreator {
  // 工厂方法
  abstract createParser(): FileParser;

  // 使用工厂方法解析并输出
  async parseAndOutput(filePath: string, outputPath: string): Promise<void> {
    const parser = this.createParser();
    const sections = await parser.parse(filePath);
    if (sections.length > 0 && sections.every(s => typeof s.title === 'string' && typeof s.paragraph === 'string')) {
      console.log(`Writing JSON output to ${outputPath}`);
      await fs.writeFile(outputPath, JSON.stringify(sections, null, 2));
    } else {
      throw new Error('Invalid content structure');
    }
  }
}

// 具体创建者:Markdown 解析器工厂
class MarkdownParserCreator extends ParserCreator {
  createParser(): FileParser {
    return new MarkdownFileParser();
  }
}

// 具体创建者:PDF 解析器工厂
class PDFParserCreator extends ParserCreator {
  createParser(): FileParser {
    return new PDFFileParser();
  }
}

// 具体创建者:Word 解析器工厂
class WordParserCreator extends ParserCreator {
  createParser(): FileParser {
    return new WordFileParser();
  }
}

// 工厂方法选择器:根据文件扩展名选择工厂
class ParserFactory {
  static getParserCreator(filePath: string): ParserCreator {
    const ext = path.extname(filePath).toLowerCase();
    switch (ext) {
      case '.md':
        return new MarkdownParserCreator();
      case '.pdf':
        return new PDFParserCreator();
      case '.docx':
        return new WordParserCreator();
      default:
        throw new Error(`Unsupported file extension: ${ext}`);
    }
  }
}

// 客户端代码
async function main() {
  // 假设输入文件存在
  const files = [
    { path: 'knowledge.md', output: 'output_md.json' },
    { path: 'knowledge.pdf', output: 'output_pdf.json' },
    { path: 'knowledge.docx', output: 'output_docx.json' }
  ];

  for (const file of files) {
    try {
      const creator = ParserFactory.getParserCreator(file.path);
      await creator.parseAndOutput(file.path, file.output);
    } catch (error) {
      console.error(`Error processing ${file.path}:`, error);
    }
  }
}

main().catch(console.error);

运行与测试

  1. 准备 :安装依赖(npm install marked pdf-parse mammoth @types/node)并准备测试文件:

    • knowledge.md: # Title\nParagraph text
    • knowledge.pdf: PDF 文件(假设包含简单文本)。
    • knowledge.docx: Word 文件(假设包含段落)。
  2. 运行 :执行 npx ts-node parser.ts(假设文件名为 parser.ts)。

  3. 输出 :生成 JSON 文件(如 output_md.json),包含提取的 sections。

  4. 控制台输出示例

    javascript 复制代码
    Parsing Markdown file: knowledge.md
    Writing JSON output to output_md.json
    Parsing PDF file: knowledge.pdf
    Writing JSON output to output_pdf.json
    Parsing Word file: knowledge.docx
    Writing JSON output to output_docx.json

代码说明

  • 工厂方法createParser 定义在抽象类 ParserCreator 中,由具体工厂实现,创建对应解析器。
  • 产品接口FileParser 定义解析行为,具体解析器(如 MarkdownFileParser)实现逻辑。
  • 工厂选择器ParserFactory 根据文件扩展名动态选择工厂,增强扩展性。
  • 现实场景:在 AI 知识库中,此模式适合动态处理多种文档格式,提取内容用于向量嵌入或搜索索引。

总结

工厂方法模式通过将对象创建延迟到子类,提供了解耦和扩展性。从简单文档生成器到 AI 知识库文件解析示例,展示了工厂方法模式在动态选择解析器场景中的应用。结合 TypeScript 的类型安全和 Node.js 的文件处理,工厂方法模式适用于需要灵活创建对象的场景,如文档处理或组件生成。

相关推荐
知其然亦知其所以然2 小时前
MySQL 社招必考题:如何优化查询过程中的数据访问?
后端·mysql·面试
用户4099322502122 小时前
FastAPI秒杀库存总变负数?Redis分布式锁能帮你守住底线吗
后端·ai编程·trae
平平无奇的开发仔2 小时前
# Springboot 中BeanDefinition是在什么阶段被创建成Bean的
后端
掘金酱2 小时前
🎉 2025年8月金石计划开奖公示
前端·人工智能·后端
SimonKing2 小时前
接口调用总失败?试试Spring官方重试框架Spring-Retry
java·后端·程序员
Cache技术分享2 小时前
191. Java 异常 - 捕获与处理异常
前端·后端
努力的小郑2 小时前
从一次分表实践谈起:我们真的需要复杂的分布式ID吗?
分布式·后端·面试
RoyLin2 小时前
TypeScript设计模式:模板方法模式
前端·后端·node.js
GeekAGI2 小时前
如何重复执行 curl 请求:8种实用方法详解
后端