工厂方法模式(Factory Method Pattern)是一种创建型设计模式,用于定义一个创建对象的接口,但将实际的实例化过程延迟到子类中实现。这允许子类决定实例化哪种具体类,从而实现灵活的对象创建。
什么是工厂方法模式?
工厂方法模式的核心是通过抽象工厂类定义一个创建对象的方法(工厂方法),由子类实现具体的对象创建逻辑。这样可以实现:
- 解耦:客户端代码与具体类的实例化过程分离,只依赖抽象接口。
- 扩展性:通过添加新的子类,可以轻松支持新的产品类型。
- 单一职责:将对象创建逻辑从业务逻辑中分离,增强代码清晰度。
在 TypeScript 中,工厂方法模式通过接口和抽象类结合类型系统,确保创建的对象符合预期类型。
工厂方法模式的结构
工厂方法模式通常包含以下几个角色:
- Product(产品接口):定义工厂方法创建的对象的接口。
- ConcreteProduct(具体产品):实现产品接口,定义具体的对象。
- Creator(创建者接口/抽象类):声明工厂方法,返回 Product 类型。
- 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
)和具体工厂类(PDFDocumentCreator
、WordDocumentCreator
)分离创建逻辑,支持不同文档类型的生成。
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);
运行与测试
-
准备 :安装依赖(
npm install marked pdf-parse mammoth @types/node
)并准备测试文件:knowledge.md
:# Title\nParagraph text
knowledge.pdf
: PDF 文件(假设包含简单文本)。knowledge.docx
: Word 文件(假设包含段落)。
-
运行 :执行
npx ts-node parser.ts
(假设文件名为parser.ts
)。 -
输出 :生成 JSON 文件(如
output_md.json
),包含提取的 sections。 -
控制台输出示例 :
javascriptParsing 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 的文件处理,工厂方法模式适用于需要灵活创建对象的场景,如文档处理或组件生成。