工厂模式(Factory Pattern) 是一种创建型设计模式,它定义了一个用于创建对象的接口,而不需要显式地指定对象所属的具体类。换句话说,工厂模式将对象的实例化过程延迟到子类或其他工厂方法中,通过工厂方法来创建对象而不是直接调用构造函数。
工厂模式的核心思想是将对象创建的细节隐藏在工厂方法中,从而让代码具有更好的扩展性和可维护性。
工厂模式在实际开发中有很多应用场景,下面是一些例子:
工厂模式的分类:
简单工厂模式(Simple Factory Pattern):通过一个工厂类来决定实例化哪个类的对象,通常使用一个静态方法。
工厂方法模式(Factory Method Pattern):将对象的实例化延迟到子类,通过子类重写的工厂方法来创建具体对象。
抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建一系列相关或依赖的对象,而无需指定它们的具体类。抽象工厂模式通常用来生产一系列产品,这些产品通常是关联在一起的,比如一整套家具中的桌子和椅子。
为什么会出现这三种工厂模式?
简单性与灵活性的权衡:
- 简单工厂模式提供了一种简单易用的创建对象的方式,但当需求变化时,修改工厂类会很困难。
- 工厂方法模式通过将对象的创建推迟到子类,提供了更大的灵活性,但增加了代码量。
产品种类的增加:
- 当产品种类较少时,简单工厂模式可以很好地满足需求。
- 但当产品种类不断增加时,工厂方法模式可以更好地应对这种变化,通过引入工厂子类来创建不同类型的产品。
产品之间的依赖关系:
- 当产品之间存在相互依赖或相关的关系时,简单工厂模式和工厂方法模式可能无法很好地处理这种情况。
- 抽象工厂模式通过提供一个创建相关产品家族的接口,更好地解决了这个问题。
class Car:
def __init__(self, model):
self.model = model
def drive(self):
pass
class Sedan(Car):
def drive(self):
return f"驾驶 {self.model} 轿车"
class SUV(Car):
def drive(self):
return f"驾驶 {self.model} SUV"
class CarFactory:
@staticmethod
def 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, abstractmethod
class Car(ABC):
@abstractmethod
def drive(self):
pass
class Sedan(Car):
def drive(self):
return "驾驶轿车"
class SUV(Car):
def drive(self):
return "驾驶SUV"
class CarFactory(ABC):
@abstractmethod
def create_car(self):
pass
class 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):
@abstractmethod
def drive(self):
pass
class Engine(ABC):
@abstractmethod
def 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):
@abstractmethod
def create_car(self):
pass
@abstractmethod
def 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()) # 输出: 启动电动发动机
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 Tire
const sedanEngine = sedanFactory2.createEngine();
const sedanTire = sedanFactory2.createTire();
console.log(sedanEngine.type); // 输出: Sedan Engine
console.log(sedanTire.type); // 输出: Sedan Tire
问题:在不同环境(开发、生产)中需要不同的配置设置。
解决方案:使用简单工厂来创建适合特定环境的配置对象。
# 场景:配置管理系统
class Configuration:
def __init__(self, settings):
self.settings = settings
def get_setting(self, key):
return self.settings.get(key)
class ConfigurationFactory:
@staticmethod
def 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
优点:
- 集中管理配置创建逻辑,便于维护。
- 客户端代码不需要知道具体的配置实现细节。
缺点:
- 如果需要添加新的环境配置,需要修改工厂类。
适用场景:
- 配置项相对固定,不经常变动。
- 配置逻辑相对简单,不需要复杂的继承结构。
问题:需要支持多种支付方式(如Stripe、PayPal),且可能需要经常添加新的支付方式。
解决方案:为每种支付处理器创建一个工厂类,通过工厂方法创建具体的支付处理器。
# 场景:支付系统
from abc import ABC, abstractmethod
class PaymentProcessor(ABC):
@abstractmethod
def process_payment(self, amount):
pass
class 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):
@abstractmethod
def create_processor(self):
pass
class 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
优点:
- 易于扩展新的支付方式,只需添加新的处理器类和对应的工厂类。
- 符合开闭原则,不需要修改现有代码就可以添加新的支付方式。
缺点:
- 可能会导致类的数量增加,每种支付方式都需要一个工厂类。
适用场景:
- 系统需要经常添加新的产品类型。
- 产品的创建逻辑比较复杂,需要独立的工厂类来管理。
问题:需要创建一套完整的、风格一致的 UI 组件,且这些组件需要在不同的操作系统上有不同的外观。
解决方案:使用抽象工厂来创建一系列相关的 UI 组件,为每个平台提供一个具体的工厂。
# 场景:跨平台 UI 组件库
class Button(ABC):
@abstractmethod
def paint(self):
pass
class 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):
@abstractmethod
def paint(self):
pass
class 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):
@abstractmethod
def create_button(self):
pass
@abstractmethod
def create_checkbox(self):
pass
class 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),需要修改所有的工厂类。
适用场景:
- 需要创建一系列相关或相互依赖的对象。
- 系统需要与多个产品族一起工作,但每次只使用其中一个。
前端:
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;
}
// 记录日志信息到 IndexedDB
log(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:00
if (now > nextUploadTime) {
// 如果当前时间已经超过今天的 1:00,则设置为明天的 1:00
nextUploadTime.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}`);
});