一,什么是工厂模式
工厂模式(Factory Pattern) 是一种创建型设计模式,它定义了一个用于创建对象的接口,而不需要显式地指定对象所属的具体类。换句话说,工厂模式将对象的实例化过程延迟到子类或其他工厂方法中,通过工厂方法来创建对象而不是直接调用构造函数。
工厂模式的核心思想是将对象创建的细节隐藏在工厂方法中,从而让代码具有更好的扩展性和可维护性。
- 解耦对象的创建和使用:当你想要将对象的创建与其使用分离时,工厂模式很有用。这样可以提高代码的灵活性和可维护性。
- 处理复杂的创建逻辑:当创建一个对象需要复杂的逻辑时,工厂模式可以将该逻辑封装在工厂方法中,从而简化代码。
- 创建不同类型的对象:当你需要创建不同类型的对象,并且代码不需要知道这些对象的具体类型时,工厂模式很有用。
- 支持多态:工厂模式可以通过返回抽象产品类型的对象来支持多态,从而使代码更加通用和灵活。
工厂模式在实际开发中有很多应用场景,下面是一些例子:
- 数据库连接工厂:在Web应用程序中,我们可能需要连接到不同类型的数据库(MySQL、PostgreSQL、MongoDB等)。使用工厂模式创建一个工厂类来封装创建数据库连接的逻辑,从而使客户端代码独立于具体的数据库实现。
- UI组件工厂:在GUI应用程序中,可以使用工厂模式来创建不同类型的UI组件,如按钮、文本框、下拉列表等。工厂类可以根据用户的输入或配置,返回合适的UI组件实例。
- 日志记录工厂:在应用程序中,我们可能需要使用不同的日志记录机制,如控制台输出、文件日志、 Syslog 等。使用工厂模式可以创建一个日志工厂,根据配置返回合适的日志记录器实例。
- 支付网关工厂:在电子商务应用程序中,可能需要支持多种支付方式,如信用卡、PayPal、Apple Pay等。使用工厂模式可以创建一个支付网关工厂,根据用户选择的支付方式返回相应的支付网关实例。
- 文件读写工厂:在处理文件I/O操作时,可以使用工厂模式创建不同类型的文件读写器,如CSV读写器、XML读写器、PDF读写器等。这样可以将文件格式的处理逻辑封装在工厂类中,使客户端代码更加灵活和可扩展。
- 游戏角色工厂:在游戏开发中,可以使用工厂模式来创建不同类型的游戏角色,如战士、法师、弓箭手等。每种角色都有不同的属性和技能,工厂类可以根据需求创建合适的角色实例。
- 报告生成器工厂:在企业应用程序中,可能需要生成各种类型的报告,如销售报告、财务报告、库存报告等。使用工厂模式可以创建一个报告生成器工厂,根据报告类型返回合适的报告生成器实例。
工厂模式的分类:
-
简单工厂模式(Simple Factory Pattern):通过一个工厂类来决定实例化哪个类的对象,通常使用一个静态方法。
-
工厂方法模式(Factory Method Pattern):将对象的实例化延迟到子类,通过子类重写的工厂方法来创建具体对象。
-
抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建一系列相关或依赖的对象,而无需指定它们的具体类。抽象工厂模式通常用来生产一系列产品,这些产品通常是关联在一起的,比如一整套家具中的桌子和椅子。
为什么会出现这三种工厂模式?
-
简单性与灵活性的权衡:
- 简单工厂模式提供了一种简单易用的创建对象的方式,但当需求变化时,修改工厂类会很困难。
- 工厂方法模式通过将对象的创建推迟到子类,提供了更大的灵活性,但增加了代码量。 -
产品种类的增加:
- 当产品种类较少时,简单工厂模式可以很好地满足需求。
- 但当产品种类不断增加时,工厂方法模式可以更好地应对这种变化,通过引入工厂子类来创建不同类型的产品。 -
产品之间的依赖关系:
- 当产品之间存在相互依赖或相关的关系时,简单工厂模式和工厂方法模式可能无法很好地处理这种情况。
- 抽象工厂模式通过提供一个创建相关产品家族的接口,更好地解决了这个问题。
二,python代码
(一)简单工厂模式
class Car:def __init__(self, model):self.model = modeldef drive(self):passclass Sedan(Car):def drive(self):return f"驾驶 {self.model} 轿车"class SUV(Car):def drive(self):return f"驾驶 {self.model} SUV"class CarFactory:@staticmethoddef create_car(car_type, model):if car_type == "sedan":return Sedan(model)elif car_type == "suv":return SUV(model)else:raise ValueError("不支持的车型")# 使用简单工厂
factory = CarFactory()
sedan = factory.create_car("sedan", "Toyota Camry")
suv = factory.create_car("suv", "Honda CR-V")print(sedan.drive()) # 输出: 驾驶 Toyota Camry 轿车
print(suv.drive()) # 输出: 驾驶 Honda CR-V SUV
(二)工厂方法模式
from abc import ABC, abstractmethodclass Car(ABC):@abstractmethoddef drive(self):passclass Sedan(Car):def drive(self):return "驾驶轿车"class SUV(Car):def drive(self):return "驾驶SUV"class CarFactory(ABC):@abstractmethoddef create_car(self):passclass SedanFactory(CarFactory):def create_car(self):return Sedan()class SUVFactory(CarFactory):def create_car(self):return SUV()# 使用工厂方法
sedan_factory = SedanFactory()
suv_factory = SUVFactory()sedan = sedan_factory.create_car()
suv = suv_factory.create_car()print(sedan.drive()) # 输出: 驾驶轿车
print(suv.drive()) # 输出: 驾驶SUV
(三)抽象工厂模式
from abc import ABC, abstractmethod# 抽象产品
class Car(ABC):@abstractmethoddef drive(self):passclass Engine(ABC):@abstractmethoddef start(self):pass# 具体产品
class SedanCar(Car):def drive(self):return "驾驶轿车"class SUVCar(Car):def drive(self):return "驾驶SUV"class GasolineEngine(Engine):def start(self):return "启动汽油发动机"class ElectricEngine(Engine):def start(self):return "启动电动发动机"# 抽象工厂
class CarFactory(ABC):@abstractmethoddef create_car(self):pass@abstractmethoddef create_engine(self):pass# 具体工厂
class SedanGasolineFactory(CarFactory):def create_car(self):return SedanCar()def create_engine(self):return GasolineEngine()class SUVElectricFactory(CarFactory):def create_car(self):return SUVCar()def create_engine(self):return ElectricEngine()# 使用抽象工厂
sedan_gasoline_factory = SedanGasolineFactory()
suv_electric_factory = SUVElectricFactory()sedan = sedan_gasoline_factory.create_car()
sedan_engine = sedan_gasoline_factory.create_engine()suv = suv_electric_factory.create_car()
suv_engine = suv_electric_factory.create_engine()print(sedan.drive()) # 输出: 驾驶轿车
print(sedan_engine.start()) # 输出: 启动汽油发动机
print(suv.drive()) # 输出: 驾驶SUV
print(suv_engine.start()) # 输出: 启动电动发动机
三,JavaScript代码
(一)简单工厂模式
class Car {constructor(model) {this.model = model;}
}class SimpleCatFactory {createCar(model) {switch (model) {case 'SUV':return new Car('SUV');case 'Sedan':return new Car('Sedan');default:throw new Error('Unknown car model');}}
}// 使用简单工厂
const simpleFactory = new SimpleCatFactory();
const suv = simpleFactory.createCar('SUV');
console.log(suv.model); // 输出: SUV
(二)工厂方法模式
class CarFactory {createCar() {throw new Error('This method should be overridden');}
}class SUVFactory extends CarFactory {createCar() {return new Car('SUV');}
}class SedanFactory extends CarFactory {createCar() {return new Car('Sedan');}
}// 使用工厂方法
const suvFactory = new SUVFactory();
const sedanFactory = new SedanFactory();
const suv2 = suvFactory.createCar();
const sedan = sedanFactory.createCar();
console.log(suv2.model); // 输出: SUV
console.log(sedan.model); // 输出: Sedan
(三)抽象工厂模式
class Engine {constructor(type) {this.type = type;}
}class Tire {constructor(type) {this.type = type;}
}class AbstractCarFactory {createEngine() {throw new Error('This method should be overridden');}createTire() {throw new Error('This method should be overridden');}
}class SUVFactory extends AbstractCarFactory {createEngine() {return new Engine('SUV Engine');}createTire() {return new Tire('SUV Tire');}
}class SedanFactory extends AbstractCarFactory {createEngine() {return new Engine('Sedan Engine');}createTire() {return new Tire('Sedan Tire');}
}// 使用抽象工厂
const suvFactory2 = new SUVFactory();
const sedanFactory2 = new SedanFactory();const suvEngine = suvFactory2.createEngine();
const suvTire = suvFactory2.createTire();
console.log(suvEngine.type); // 输出: SUV Engine
console.log(suvTire.type); // 输出: SUV Tireconst sedanEngine = sedanFactory2.createEngine();
const sedanTire = sedanFactory2.createTire();
console.log(sedanEngine.type); // 输出: Sedan Engine
console.log(sedanTire.type); // 输出: Sedan Tire
四,实际应用
(一)python代码
1,配置管理系统
问题:在不同环境(开发、生产)中需要不同的配置设置。
解决方案:使用简单工厂来创建适合特定环境的配置对象。
# 场景:配置管理系统class Configuration:def __init__(self, settings):self.settings = settingsdef get_setting(self, key):return self.settings.get(key)class ConfigurationFactory:@staticmethoddef create_configuration(env):if env == "development":return Configuration({"debug": True})elif env == "production":return Configuration({"debug": False})else:raise ValueError("Invalid environment")# 使用简单工厂
dev_config = ConfigurationFactory.create_configuration("development")
print(dev_config.get_setting("debug")) # 输出: True
优点:
- 集中管理配置创建逻辑,便于维护。
- 客户端代码不需要知道具体的配置实现细节。
缺点:
- 如果需要添加新的环境配置,需要修改工厂类。
适用场景:
- 配置项相对固定,不经常变动。
- 配置逻辑相对简单,不需要复杂的继承结构。
2,支付系统
问题:需要支持多种支付方式(如Stripe、PayPal),且可能需要经常添加新的支付方式。
解决方案:为每种支付处理器创建一个工厂类,通过工厂方法创建具体的支付处理器。
# 场景:支付系统from abc import ABC, abstractmethodclass PaymentProcessor(ABC):@abstractmethoddef process_payment(self, amount):passclass StripeProcessor(PaymentProcessor):def process_payment(self, amount):print(f"Processing ${amount} payment via Stripe")class PayPalProcessor(PaymentProcessor):def process_payment(self, amount):print(f"Processing ${amount} payment via PayPal")class PaymentProcessorFactory(ABC):@abstractmethoddef create_processor(self):passclass StripeProcessorFactory(PaymentProcessorFactory):def create_processor(self):return StripeProcessor()class PayPalProcessorFactory(PaymentProcessorFactory):def create_processor(self):return PayPalProcessor()# 使用工厂方法
stripe_factory = StripeProcessorFactory()
stripe_processor = stripe_factory.create_processor()
stripe_processor.process_payment(100) # 输出: Processing $100 payment via Stripe
优点:
- 易于扩展新的支付方式,只需添加新的处理器类和对应的工厂类。
- 符合开闭原则,不需要修改现有代码就可以添加新的支付方式。
缺点:
- 可能会导致类的数量增加,每种支付方式都需要一个工厂类。
适用场景:
- 系统需要经常添加新的产品类型。
- 产品的创建逻辑比较复杂,需要独立的工厂类来管理。
3,跨平台 UI 组件库
问题:需要创建一套完整的、风格一致的 UI 组件,且这些组件需要在不同的操作系统上有不同的外观。
解决方案:使用抽象工厂来创建一系列相关的 UI 组件,为每个平台提供一个具体的工厂。
# 场景:跨平台 UI 组件库class Button(ABC):@abstractmethoddef paint(self):passclass MacButton(Button):def paint(self):return "Rendering a button in macOS style"class WindowsButton(Button):def paint(self):return "Rendering a button in Windows style"class Checkbox(ABC):@abstractmethoddef paint(self):passclass MacCheckbox(Checkbox):def paint(self):return "Rendering a checkbox in macOS style"class WindowsCheckbox(Checkbox):def paint(self):return "Rendering a checkbox in Windows style"class GUIFactory(ABC):@abstractmethoddef create_button(self):pass@abstractmethoddef create_checkbox(self):passclass MacFactory(GUIFactory):def create_button(self):return MacButton()def create_checkbox(self):return MacCheckbox()class WindowsFactory(GUIFactory):def create_button(self):return WindowsButton()def create_checkbox(self):return WindowsCheckbox()# 使用抽象工厂
def create_ui(factory):button = factory.create_button()checkbox = factory.create_checkbox()print(button.paint())print(checkbox.paint())mac_factory = MacFactory()
create_ui(mac_factory)
# 输出:
# Rendering a button in macOS style
# Rendering a checkbox in macOS style
优点:
- 确保创建的 UI 组件之间风格一致。
- 易于切换整个产品族(如从 Windows 风格切换到 macOS 风格)。
缺点:
- 如果需要添加新的 UI 组件类型(如 RadioButton),需要修改所有的工厂类。
适用场景:
- 需要创建一系列相关或相互依赖的对象。
- 系统需要与多个产品族一起工作,但每次只使用其中一个。
(二)JavaScript代码
1,前端日志记录系统
前端:
function initDB() {return new Promise((resolve, reject) => {const request = indexedDB.open('LogDatabase', 1);request.onupgradeneeded = (event) => {const db = event.target.result;if (!db.objectStoreNames.contains('logs')) {db.createObjectStore('logs', {autoIncrement: true});}};request.onsuccess = (event) => {resolve(event.target.result);};request.onerror = (event) => {reject('Database error: ' + event.target.errorCode);};});
}class Logger {constructor(db) {this.db = db;}// 记录日志信息到 IndexedDBlog(message, level = 'info') {const logEntry = {timestamp: new Date().toISOString(),level: level.toUpperCase(),message,};const transaction = this.db.transaction(['logs'], 'readwrite');const store = transaction.objectStore('logs');store.add(logEntry);console.log(`[${logEntry.timestamp}] [${logEntry.level}]: ${logEntry.message}`);}info(message) {this.log(message, 'info');}warning(message) {this.log(message, 'warning');}error(message) {this.log(message, 'error');}debug(message) {this.log(message, 'debug');}// 从 IndexedDB 中读取所有日志并生成文本文件async getLogsAsText() {return new Promise((resolve, reject) => {const transaction = this.db.transaction(['logs'], 'readonly');const store = transaction.objectStore('logs');const request = store.getAll();request.onsuccess = (event) => {const logs = event.target.result;const logText = logs.map(log => `[${log.timestamp}] [${log.level}]: ${log.message}`).join('\n');resolve(logText);};request.onerror = (event) => {reject('Failed to retrieve logs: ' + event.target.errorCode);};});}// 上传日志到服务器并重试三次async uploadLogs(serverUrl, retryCount = 3) {try {const logText = await this.getLogsAsText();const logBlob = new Blob([logText], {type: 'text/plain'});const formData = new FormData();formData.append('file', logBlob, 'logs.txt');const response = await fetch(serverUrl, {method: 'POST',body: formData,});if (response.ok) {console.log('Logs uploaded successfully');this.clearLogs(); // 上传成功后清除日志} else {throw new Error(`Failed to upload logs: ${response.statusText}`);}} catch (error) {console.error(error);if (retryCount > 0) {console.log(`Retrying... (${3 - retryCount + 1}/3)`);await this.uploadLogs(serverUrl, retryCount - 1);} else {console.error('Failed to upload logs after 3 attempts.');}}}// 清空 IndexedDB 中的日志clearLogs() {const transaction = this.db.transaction(['logs'], 'readwrite');const store = transaction.objectStore('logs');store.clear();console.log('Logs cleared from IndexedDB');}// 每天定时上传scheduleUpload(serverUrl) {const now = new Date();const nextUploadTime = new Date();nextUploadTime.setHours(1, 0, 0, 0); // 设置为第二天的凌晨 1:00if (now > nextUploadTime) {// 如果当前时间已经超过今天的 1:00,则设置为明天的 1:00nextUploadTime.setDate(nextUploadTime.getDate() + 1);}const timeUntilNextUpload = nextUploadTime.getTime() - now.getTime();console.log(`Next upload scheduled in ${(timeUntilNextUpload / 1000 / 60).toFixed(2)} minutes`);setTimeout(() => {this.uploadLogs(serverUrl);setInterval(() => {this.uploadLogs(serverUrl);}, 24 * 60 * 60 * 1000); // 每天上传一次}, timeUntilNextUpload);}
}const config = {uploadLogs: true, // 配置项:是否上传日志serverUrl: 'https://example.com/upload', // 后端服务器地址
};initDB().then(db => {const logger = new Logger(db);// 记录不同级别的日志logger.info('This is an info log.');logger.warning('This is a warning log.');logger.error('This is an error log.');logger.debug('This is a debug log.');// 每天定时上传日志if (config.uploadLogs) {logger.scheduleUpload(config.serverUrl);}
}).catch(error => {console.error('Failed to initialize IndexedDB:', error);
});
node.js 后端:
const express = require('express');
const multer = require('multer');
const app = express();
const port = 3000;const upload = multer({dest: 'uploads/'}); // 上传文件的目录app.post('/upload', upload.single('file'), (req, res) => {console.log('File uploaded:', req.file);res.status(200).send('File uploaded successfully');
});app.listen(port, () => {console.log(`Server running at http://localhost:${port}`);
});