目录¶
1. 什么是工厂方法模式?¶
工厂方法模式 是一种创建型设计模式,定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。这种模式通过将对象的创建过程委托给子类,实现了代码的灵活性和可扩展性。
关键点:
封装对象创建:将对象的创建过程封装在工厂类中,客户端无需知道具体的创建细节。
解耦:客户端与具体产品类解耦,降低系统的耦合度。
扩展性:通过增加新的工厂子类和产品类,轻松扩展系统功能。
2. 工厂方法模式的意图¶
定义一个创建对象的接口:让子类决定实例化哪一个类。
实现类的解耦:客户端无需知道具体类的实例化过程,只需通过工厂接口获取对象。
提高系统的可扩展性:通过添加新的工厂子类和产品类,系统可以灵活扩展。
3. 工厂方法模式的适用场景¶
对象的创建需要依赖具体类:当一个类无法提前确定它所需要的对象的具体类型时。
代码需要遵循开放-封闭原则:系统需要对扩展开放,对修改封闭。
需要管理和控制对象的创建:例如,单例对象、池化对象等。
客户端不关心对象的创建过程:客户端只需要通过工厂接口获取对象,而无需了解创建细节。
4. 工厂方法模式的结构¶
工厂方法模式主要由以下几个角色组成:
产品(Product):定义工厂方法所创建的对象的接口或抽象类。
具体产品(ConcreteProduct):实现产品接口的具体类。
工厂(Creator):声明工厂方法,返回一个产品对象。可以包含一个默认的工厂方法实现。
具体工厂(ConcreteCreator):实现工厂方法,返回具体产品的实例。
UML 类图示例:
+------------------+ +------------------+
| Creator |<>-----| Product |
+------------------+ +------------------+
| + factoryMethod()| | + operation() |
+------------------+ +------------------+
/_\
|
+----------------------+ +----------------------+
| ConcreteCreatorA | | ConcreteCreatorB |
+----------------------+ +----------------------+
| + factoryMethod() | | + factoryMethod() |
+----------------------+ +----------------------+
| |
+------------------+ +------------------+
| ConcreteProductA | | ConcreteProductB |
+------------------+ +------------------+
| + operation() | | + operation() |
+------------------+ +------------------+
5. 工厂方法模式的实现¶
工厂方法模式有多种实现方式,以下介绍几种常见的实现方式,并提供相应的代码示例。
5.1. 经典工厂方法实现¶
概述: 经典的工厂方法模式通过定义一个工厂接口或抽象类,声明一个工厂方法,由具体工厂子类实现该方法,返回具体产品的实例。
代码示例:
Java - 经典工厂方法实现
// 产品接口
public interface Product {
void use();
}
// 具体产品类 A
public class ConcreteProductA implements Product {
public void use() {
System.out.println("使用具体产品 A");
}
}
// 具体产品类 B
public class ConcreteProductB implements Product {
public void use() {
System.out.println("使用具体产品 B");
}
}
// 工厂接口
public abstract class Factory {
public abstract Product factoryMethod();
}
// 具体工厂类 A
public class ConcreteFactoryA extends Factory {
public Product factoryMethod() {
return new ConcreteProductA();
}
}
// 具体工厂类 B
public class ConcreteFactoryB extends Factory {
public Product factoryMethod() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.factoryMethod();
productA.use(); // 输出: 使用具体产品 A
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.factoryMethod();
productB.use(); // 输出: 使用具体产品 B
}
}
Python - 经典工厂方法实现
from abc import ABC, abstractmethod
# 产品接口
class Product(ABC):
@abstractmethod
def use(self):
pass
# 具体产品类 A
class ConcreteProductA(Product):
def use(self):
print("使用具体产品 A")
# 具体产品类 B
class ConcreteProductB(Product):
def use(self):
print("使用具体产品 B")
# 工厂接口
class Factory(ABC):
@abstractmethod
def factory_method(self) -> Product:
pass
# 具体工厂类 A
class ConcreteFactoryA(Factory):
def factory_method(self) -> Product:
return ConcreteProductA()
# 具体工厂类 B
class ConcreteFactoryB(Factory):
def factory_method(self) -> Product:
return ConcreteProductB()
# 客户端代码
def client_code(factory: Factory):
product = factory.factory_method()
product.use()
if __name__ == "__main__":
factory_a = ConcreteFactoryA()
client_code(factory_a) # 输出: 使用具体产品 A
factory_b = ConcreteFactoryB()
client_code(factory_b) # 输出: 使用具体产品 B
C# - 经典工厂方法实现
using System;
// 产品接口
public interface IProduct
{
void Use();
}
// 具体产品类 A
public class ConcreteProductA : IProduct
{
public void Use()
{
Console.WriteLine("使用具体产品 A");
}
}
// 具体产品类 B
public class ConcreteProductB : IProduct
{
public void Use()
{
Console.WriteLine("使用具体产品 B");
}
}
// 工厂接口
public abstract class Factory
{
public abstract IProduct FactoryMethod();
}
// 具体工厂类 A
public class ConcreteFactoryA : Factory
{
public override IProduct FactoryMethod()
{
return new ConcreteProductA();
}
}
// 具体工厂类 B
public class ConcreteFactoryB : Factory
{
public override IProduct FactoryMethod()
{
return new ConcreteProductB();
}
}
// 客户端代码
public class Client
{
public static void Main(string[] args)
{
Factory factoryA = new ConcreteFactoryA();
IProduct productA = factoryA.FactoryMethod();
productA.Use(); // 输出: 使用具体产品 A
Factory factoryB = new ConcreteFactoryB();
IProduct productB = factoryB.FactoryMethod();
productB.Use(); // 输出: 使用具体产品 B
}
}
优缺点分析
优点:
解耦:客户端与具体产品类解耦,客户端只依赖于产品接口。
可扩展性:新增产品类和对应工厂类,无需修改现有代码,符合开放-封闭原则。
灵活性:通过不同的工厂子类,可以灵活地创建不同类型的产品。
缺点:
类的数量增加:每增加一个产品类,需要对应增加一个工厂类,可能导致类的数量膨胀。
复杂性:对于简单对象创建,使用工厂方法模式可能显得过于复杂。
5.2. 使用抽象工厂方法¶
概述: 抽象工厂方法模式(Abstract Factory Pattern)是工厂方法模式的扩展,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。它提供一个接口,用于创建一组相关或依赖的对象。
代码示例:
Java - 抽象工厂方法实现
// 抽象产品接口
public interface Button {
void render();
}
public interface Checkbox {
void render();
}
// 具体产品类 - Windows
public class WindowsButton implements Button {
public void render() {
System.out.println("渲染 Windows 按钮");
}
}
public class WindowsCheckbox implements Checkbox {
public void render() {
System.out.println("渲染 Windows 复选框");
}
}
// 具体产品类 - MacOS
public class MacOSButton implements Button {
public void render() {
System.out.println("渲染 MacOS 按钮");
}
}
public class MacOSCheckbox implements Checkbox {
public void render() {
System.out.println("渲染 MacOS 复选框");
}
}
// 抽象工厂接口
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// 具体工厂类 - Windows
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// 具体工厂类 - MacOS
public class MacOSFactory implements GUIFactory {
public Button createButton() {
return new MacOSButton();
}
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}
// 客户端代码
public class Client {
private Button button;
private Checkbox checkbox;
public Client(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void render() {
button.render();
checkbox.render();
}
public static void main(String[] args) {
GUIFactory factory;
String osName = System.getProperty("os.name").toLowerCase();
if(osName.contains("win")) {
factory = new WindowsFactory();
} else {
factory = new MacOSFactory();
}
Client client = new Client(factory);
client.render();
}
}
Python - 抽象工厂方法实现
from abc import ABC, abstractmethod
# 抽象产品接口
class Button(ABC):
@abstractmethod
def render(self):
pass
class Checkbox(ABC):
@abstractmethod
def render(self):
pass
# 具体产品类 - Windows
class WindowsButton(Button):
def render(self):
print("渲染 Windows 按钮")
class WindowsCheckbox(Checkbox):
def render(self):
print("渲染 Windows 复选框")
# 具体产品类 - MacOS
class MacOSButton(Button):
def render(self):
print("渲染 MacOS 按钮")
class MacOSCheckbox(Checkbox):
def render(self):
print("渲染 MacOS 复选框")
# 抽象工厂接口
class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_checkbox(self) -> Checkbox:
pass
# 具体工厂类 - Windows
class WindowsFactory(GUIFactory):
def create_button(self) -> Button:
return WindowsButton()
def create_checkbox(self) -> Checkbox:
return WindowsCheckbox()
# 具体工厂类 - MacOS
class MacOSFactory(GUIFactory):
def create_button(self) -> Button:
return MacOSButton()
def create_checkbox(self) -> Checkbox:
return MacOSCheckbox()
# 客户端代码
class Client:
def __init__(self, factory: GUIFactory):
self.button = factory.create_button()
self.checkbox = factory.create_checkbox()
def render(self):
self.button.render()
self.checkbox.render()
if __name__ == "__main__":
import platform
os_name = platform.system().lower()
if "windows" in os_name:
factory = WindowsFactory()
else:
factory = MacOSFactory()
client = Client(factory)
client.render()
C# - 抽象工厂方法实现
using System;
// 抽象产品接口
public interface IButton
{
void Render();
}
public interface ICheckbox
{
void Render();
}
// 具体产品类 - Windows
public class WindowsButton : IButton
{
public void Render()
{
Console.WriteLine("渲染 Windows 按钮");
}
}
public class WindowsCheckbox : ICheckbox
{
public void Render()
{
Console.WriteLine("渲染 Windows 复选框");
}
}
// 具体产品类 - MacOS
public class MacOSButton : IButton
{
public void Render()
{
Console.WriteLine("渲染 MacOS 按钮");
}
}
public class MacOSCheckbox : ICheckbox
{
public void Render()
{
Console.WriteLine("渲染 MacOS 复选框");
}
}
// 抽象工厂接口
public interface IGUIFactory
{
IButton CreateButton();
ICheckbox CreateCheckbox();
}
// 具体工厂类 - Windows
public class WindowsFactory : IGUIFactory
{
public IButton CreateButton()
{
return new WindowsButton();
}
public ICheckbox CreateCheckbox()
{
return new WindowsCheckbox();
}
}
// 具体工厂类 - MacOS
public class MacOSFactory : IGUIFactory
{
public IButton CreateButton()
{
return new MacOSButton();
}
public ICheckbox CreateCheckbox()
{
return new MacOSCheckbox();
}
}
// 客户端代码
public class Client
{
private IButton button;
private ICheckbox checkbox;
public Client(IGUIFactory factory)
{
button = factory.CreateButton();
checkbox = factory.CreateCheckbox();
}
public void Render()
{
button.Render();
checkbox.Render();
}
public static void Main(string[] args)
{
IGUIFactory factory;
string osName = Environment.OSVersion.Platform.ToString().ToLower();
if(osName.Contains("win"))
{
factory = new WindowsFactory();
}
else
{
factory = new MacOSFactory();
}
Client client = new Client(factory);
client.Render();
}
}
优缺点分析
优点:
解耦:客户端与具体产品类解耦,依赖于抽象工厂和抽象产品接口。
可扩展性:易于添加新的产品族,只需新增具体工厂和具体产品类。
统一管理:相关产品的创建集中在工厂类中,便于管理和维护。
缺点:
类的数量增加:每新增一个产品族,需要增加对应的工厂和产品类,可能导致类的数量膨胀。
复杂性增加:相对于简单的工厂方法模式,抽象工厂模式实现更为复杂。
5.3. 使用反射实现工厂方法¶
概述: 利用反射机制动态创建对象,减少工厂类的数量,提高灵活性。但需要注意安全性和性能问题。
代码示例:
Java - 使用反射实现工厂方法
import java.lang.reflect.Constructor;
// 产品接口
public interface Product {
void use();
}
// 具体产品类
public class ConcreteProductA implements Product {
public void use() {
System.out.println("使用具体产品 A");
}
}
public class ConcreteProductB implements Product {
public void use() {
System.out.println("使用具体产品 B");
}
}
// 工厂类
public class ProductFactory {
public static Product createProduct(String className) {
try {
Class<?> cls = Class.forName(className);
Constructor<?> constructor = cls.getDeclaredConstructor();
return (Product) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Product productA = ProductFactory.createProduct("ConcreteProductA");
if(productA != null) {
productA.use(); // 输出: 使用具体产品 A
}
Product productB = ProductFactory.createProduct("ConcreteProductB");
if(productB != null) {
productB.use(); // 输出: 使用具体产品 B
}
}
}
优缺点分析
优点:
灵活性高:无需在工厂类中显式地指定具体产品类,通过类名动态创建。
减少工厂类数量:一个通用的工厂方法可以创建所有类型的产品。
缺点:
安全性问题:使用反射可能绕过访问控制,带来安全隐患。
性能开销:反射操作相比直接实例化有更高的性能开销。
代码维护困难:类名字符串的使用容易出错,且不易重构。
6. 工厂方法模式的优缺点¶
优点:
解耦:客户端代码与具体产品类解耦,只依赖于工厂接口和产品接口。
灵活性和可扩展性:通过添加新的具体工厂和产品类,可以轻松扩展系统功能,而无需修改现有代码。
遵循开闭原则:系统对扩展开放,对修改封闭,符合 SOLID 原则。
集中管理对象创建:所有对象的创建过程集中在工厂类中,便于管理和维护。
缺点:
类的数量增加:每增加一个产品类,需要对应增加一个具体工厂类,可能导致系统中类的数量急剧增加,增加了系统的复杂性。
实现复杂:相比于简单的对象创建,工厂方法模式的实现更为复杂,尤其是在产品和工厂类较多的情况下。
子类依赖:依赖于工厂子类,可能导致工厂层次结构的复杂性增加。
7. 工厂方法模式的常见误区与解决方案¶
误区1:工厂方法模式适用于所有对象创建场景
事实:工厂方法模式适用于需要延迟到子类决定实例化哪一个类的场景。对于简单的对象创建,使用直接实例化可能更简单高效。
误区2:工厂方法模式可以减少代码量
事实:工厂方法模式通过增加工厂类和具体工厂类来解耦,但这也可能导致类的数量增加,并不一定减少代码量。
误区3:工厂方法模式无法与其他设计模式结合使用
事实:工厂方法模式可以与其他设计模式结合使用,如单例模式、装饰者模式等,以增强系统的设计能力。
解决方案:
评估需求:在决定是否使用工厂方法模式之前,评估系统的需求和复杂性,确保模式的使用能够带来实际的好处。
适度使用:避免过度设计,只有在确实需要解耦和扩展性的场景下才使用工厂方法模式。
结合其他模式:根据具体需求,将工厂方法模式与其他设计模式结合使用,发挥各自的优势。
8. 工厂方法模式的扩展与变体¶
抽象工厂模式(Abstract Factory Pattern)
定义:创建一系列相关或相互依赖的对象,而无需指定它们的具体类。
应用场景:需要创建多个相关对象的产品族时,如跨平台UI组件的创建。
区别:工厂方法模式侧重于创建一个产品,而抽象工厂模式侧重于创建一系列相关产品。
多工厂模式(Multiple Factory Pattern)
定义:系统中存在多个工厂类,每个工厂类负责创建不同类型的产品。
应用场景:大型系统中,按功能模块划分工厂,便于管理和扩展。
工厂模式与单例模式结合
定义:将工厂类设计为单例,确保系统中只有一个工厂实例,集中管理对象创建。
应用场景:对象创建需要集中控制的场景,如数据库连接池的工厂。
9. 工厂方法模式的应用实例¶
实例1:日志记录器
Java 实现
// 产品接口
public interface Logger {
void log(String message);
}
// 具体产品类 - 文件日志记录器
public class FileLogger implements Logger {
public void log(String message) {
System.out.println("写入文件日志: " + message);
// 实际代码中应写入文件
}
}
// 具体产品类 - 控制台日志记录器
public class ConsoleLogger implements Logger {
public void log(String message) {
System.out.println("控制台日志: " + message);
}
}
// 工厂接口
public abstract class LoggerFactory {
public abstract Logger createLogger();
}
// 具体工厂类 - 文件日志工厂
public class FileLoggerFactory extends LoggerFactory {
public Logger createLogger() {
return new FileLogger();
}
}
// 具体工厂类 - 控制台日志工厂
public class ConsoleLoggerFactory extends LoggerFactory {
public Logger createLogger() {
return new ConsoleLogger();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
LoggerFactory factory;
String logType = "console"; // 可以通过配置文件获取
if(logType.equalsIgnoreCase("file")) {
factory = new FileLoggerFactory();
} else {
factory = new ConsoleLoggerFactory();
}
Logger logger = factory.createLogger();
logger.log("这是一个日志消息");
}
}
输出:
控制台日志: 这是一个日志消息
实例2:图形编辑器
Python 实现
from abc import ABC, abstractmethod
# 产品接口
class Shape(ABC):
@abstractmethod
def draw(self):
pass
# 具体产品类 - 圆形
class Circle(Shape):
def draw(self):
print("绘制一个圆形")
# 具体产品类 - 矩形
class Rectangle(Shape):
def draw(self):
print("绘制一个矩形")
# 工厂接口
class ShapeFactory(ABC):
@abstractmethod
def create_shape(self) -> Shape:
pass
# 具体工厂类 - 圆形工厂
class CircleFactory(ShapeFactory):
def create_shape(self) -> Shape:
return Circle()
# 具体工厂类 - 矩形工厂
class RectangleFactory(ShapeFactory):
def create_shape(self) -> Shape:
return Rectangle()
# 客户端代码
def client_code(factory: ShapeFactory):
shape = factory.create_shape()
shape.draw()
if __name__ == "__main__":
factory_type = "circle" # 可以通过用户输入或配置文件获取
if factory_type == "circle":
factory = CircleFactory()
elif factory_type == "rectangle":
factory = RectangleFactory()
else:
raise ValueError("未知的工厂类型")
client_code(factory) # 输出: 绘制一个圆形
实例3:数据库连接
C# 实现
using System;
// 产品接口
public interface IDatabaseConnection
{
void Connect();
}
// 具体产品类 - MySQL连接
public class MySQLConnection : IDatabaseConnection
{
public void Connect()
{
Console.WriteLine("连接到 MySQL 数据库");
// 实际代码中应实现连接逻辑
}
}
// 具体产品类 - SQL Server连接
public class SQLServerConnection : IDatabaseConnection
{
public void Connect()
{
Console.WriteLine("连接到 SQL Server 数据库");
// 实际代码中应实现连接逻辑
}
}
// 工厂接口
public abstract class DatabaseConnectionFactory
{
public abstract IDatabaseConnection CreateConnection();
}
// 具体工厂类 - MySQL工厂
public class MySQLConnectionFactory : DatabaseConnectionFactory
{
public override IDatabaseConnection CreateConnection()
{
return new MySQLConnection();
}
}
// 具体工厂类 - SQL Server工厂
public class SQLServerConnectionFactory : DatabaseConnectionFactory
{
public override IDatabaseConnection CreateConnection()
{
return new SQLServerConnection();
}
}
// 客户端代码
public class Client
{
public static void Main(string[] args)
{
DatabaseConnectionFactory factory;
string dbType = "mysql"; // 可以通过配置文件获取
if(dbType.Equals("mysql", StringComparison.OrdinalIgnoreCase))
{
factory = new MySQLConnectionFactory();
}
else if(dbType.Equals("sqlserver", StringComparison.OrdinalIgnoreCase))
{
factory = new SQLServerConnectionFactory();
}
else
{
throw new ArgumentException("未知的数据库类型");
}
IDatabaseConnection connection = factory.CreateConnection();
connection.Connect(); // 输出: 连接到 MySQL 数据库
}
}
10. 工厂方法模式的最佳实践¶
明确职责:
工厂类专注于对象的创建,不应承担其他业务逻辑。
遵循开闭原则:
通过新增工厂子类和产品类,扩展系统功能,无需修改现有代码。
使用接口和抽象类:
定义产品和工厂的接口或抽象类,确保系统的灵活性和可扩展性。
避免工厂类过于庞大:
将不同产品族的工厂类分开,避免单一工厂类承担过多职责。
结合依赖注入:
使用依赖注入框架管理工厂类,提升代码的可测试性和灵活性。
考虑使用反射:
在需要高灵活性的场景下,可以结合反射机制动态创建对象,但需注意安全性和性能。
11. 总结¶
工厂方法模式 是一种强大的创建型设计模式,通过定义一个用于创建对象的接口,将对象的创建过程委托给子类,实现了代码的解耦和系统的高可扩展性。它广泛应用于各种软件开发场景,如日志记录器、图形编辑器、数据库连接等。
关键学习点回顾:
理解工厂方法模式的核心概念:定义创建对象的接口,由子类决定具体实例化的类。
掌握不同的实现方式:经典工厂方法、抽象工厂方法、使用反射等。
识别适用的应用场景:对象创建需要灵活、解耦的场景,如配置管理、日志记录、数据库连接等。
认识工厂方法模式的优缺点:提高代码解耦和可扩展性,同时可能增加类的数量和系统复杂性。
避免常见误区:合理评估需求,适度使用工厂方法模式,结合其他设计模式提升系统设计质量。