工厂方法模式详解:概念、实现与应用
引言
工厂方法模式是创建型设计模式中的一个重要模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法让类的实例化推迟到子类。这种模式在现代软件开发中被广泛使用,特别是在需要创建多种相似对象的场景中。
什么是工厂方法模式?
工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
核心思想
工厂方法模式的核心思想是:
- 定义创建接口:定义一个创建对象的接口
- 延迟实例化:让子类决定实例化哪个具体类
- 解耦创建与使用:将对象的创建与使用分离
为什么需要工厂方法模式?
在许多情况下,我们需要根据不同的条件创建不同的对象:
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. 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 简单工厂模式
| 特性 | 工厂方法模式 | 简单工厂模式 |
|---|---|---|
| 扩展性 | 非常好,符合开闭原则 | 较差,添加新产品需要修改工厂类 |
| 复杂度 | 较高,需要多个类 | 较低,只需要一个工厂类 |
| 耦合度 | 低,客户端依赖抽象 | 中等,客户端依赖具体工厂 |
| 适用场景 | 系统需要支持产品族扩展 | 产品类型相对固定 |
工厂方法模式的优缺点
优点
- 符合开闭原则:添加新产品时无需修改现有代码
- 符合单一职责原则:每个工厂类只负责一种产品的创建
- 良好的封装性:客户端不需要知道具体产品的创建细节
- 良好的扩展性:可以轻松添加新的产品和工厂类
缺点
- 类数量增加:每增加一个产品类,就需要增加一个对应的工厂类
- 系统复杂度增加:引入了更多的抽象和继承关系
- 客户端理解成本:需要理解工厂类和产品类之间的关系
总结
工厂方法模式是创建型设计模式中的重要模式,它通过定义一个创建对象的接口,让子类决定实例化哪个类,从而实现了对象创建与使用的分离。
通过本章的学习,我们了解了:
- 工厂方法模式的基本概念和核心思想
- 工厂方法模式的实现方式
- 工厂方法模式在实际开发中的应用场景
- 工厂方法模式与简单工厂模式的对比
- 工厂方法模式的优缺点
工厂方法模式在现代软件开发中应用广泛,特别是在需要创建多种相似对象的场景中,它可以很好地支持系统的扩展性和维护性。
在下一章中,我们将继续探讨其他创建型模式,如抽象工厂模式。