目录¶
1. 什么是简单工厂模式?¶
简单工厂模式是一种创建型设计模式,它通过一个工厂类负责创建各种类型的对象,而客户端只需要通过工厂类提供的接口来获取所需的对象,无需直接实例化具体的类。这种模式通过封装对象的创建过程,简化了客户端的代码,提高了系统的灵活性和可维护性。
关键点:
封装创建逻辑:对象的创建过程被封装在工厂类中,客户端无需了解具体类的创建细节。
简化客户端代码:客户端只需调用工厂方法即可获取所需对象,减少了直接实例化的代码量。
集中管理对象创建:所有对象的创建都由工厂类统一管理,便于维护和扩展。
2. 简单工厂模式的意图¶
封装对象创建:将对象的创建过程集中在一个工厂类中,避免客户端直接依赖具体类。
提高系统灵活性:通过修改工厂类的逻辑,可以灵活地创建不同类型的对象,而无需修改客户端代码。
简化对象创建过程:客户端无需了解对象的创建细节,只需调用工厂方法获取对象。
促进代码复用:工厂类可以复用创建对象的逻辑,避免代码重复。
3. 简单工厂模式的适用场景¶
对象创建逻辑复杂:当对象的创建过程复杂,涉及多个步骤或依赖多个参数时,简单工厂模式可以简化这一过程。
需要创建多种相似对象:当系统需要创建多种相似的对象时,使用简单工厂模式可以统一管理对象的创建。
客户端不关心具体类:客户端只需知道所需对象的类型,无需了解具体类的实现细节。
遵循开闭原则:系统需要在不修改客户端代码的情况下,通过扩展工厂类来支持新的产品类型。
示例:
图形绘制应用:根据用户选择创建不同类型的图形对象(如圆形、矩形、三角形)。
日志记录系统:根据不同的日志类型创建相应的日志记录器(如文件日志记录器、数据库日志记录器)。
数据库连接管理:根据不同的数据库类型创建相应的数据库连接对象(如MySQL连接、Oracle连接)。
4. 简单工厂模式的结构¶
简单工厂模式主要由以下几个角色组成:
工厂类(Factory):负责创建对象的类,包含一个静态方法用于根据参数创建并返回不同类型的对象。
产品接口(Product):定义产品的公共接口或抽象类。
具体产品类(ConcreteProduct):实现产品接口的具体类。
UML 类图示例:
+----------------------------------------+
| Factory |
+----------------------------------------+
| + createProduct(type: String): Product |
+----------------------------------------+
|
|
+------------------+ +------------------+
| Product |<-------| ConcreteProductA |
+------------------+ +------------------+
| + use() | | + use() |
+------------------+ +------------------+
+------------------+
| ConcreteProductB |
+------------------+
| + use() |
+------------------+
5. 简单工厂模式的实现¶
简单工厂模式的实现可以根据不同的编程语言有所差异。以下分别以Java、Python和C#为例,展示简单工厂模式的基本实现。
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 class SimpleFactory {
public static Product createProduct(String type) {
if (type.equalsIgnoreCase("A")) {
return new ConcreteProductA();
} else if (type.equalsIgnoreCase("B")) {
return new ConcreteProductB();
}
throw new IllegalArgumentException("未知的产品类型: " + type);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.use(); // 输出: 使用具体产品A
Product productB = SimpleFactory.createProduct("B");
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 SimpleFactory:
@staticmethod
def create_product(product_type: str) -> Product:
if product_type.upper() == "A":
return ConcreteProductA()
elif product_type.upper() == "B":
return ConcreteProductB()
else:
raise ValueError(f"未知的产品类型: {product_type}")
# 客户端代码
if __name__ == "__main__":
factory = SimpleFactory()
product_a = factory.create_product("A")
product_a.use() # 输出: 使用具体产品A
product_b = factory.create_product("B")
product_b.use() # 输出: 使用具体产品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 class SimpleFactory
{
public static IProduct CreateProduct(string type)
{
if (type.Equals("A", StringComparison.OrdinalIgnoreCase))
{
return new ConcreteProductA();
}
else if (type.Equals("B", StringComparison.OrdinalIgnoreCase))
{
return new ConcreteProductB();
}
throw new ArgumentException("未知的产品类型: " + type);
}
}
// 客户端代码
public class Client
{
public static void Main(string[] args)
{
IProduct productA = SimpleFactory.CreateProduct("A");
productA.Use(); // 输出: 使用具体产品A
IProduct productB = SimpleFactory.CreateProduct("B");
productB.Use(); // 输出: 使用具体产品B
}
}
5.2. 使用内置库实现¶
某些编程语言提供了内置的工厂机制或支持反射,可以进一步简化简单工厂模式的实现。以下以Python中的动态类加载和C#中的反射机制为例,展示如何利用内置特性实现简单工厂模式。
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 SimpleFactory:
@staticmethod
def create_product(product_type: str) -> Product:
try:
product_class = globals()[f"ConcreteProduct{product_type.upper()}"]
return product_class()
except KeyError:
raise ValueError(f"未知的产品类型: {product_type}")
# 客户端代码
if __name__ == "__main__":
factory = SimpleFactory()
product_a = factory.create_product("A")
product_a.use() # 输出: 使用具体产品A
product_b = factory.create_product("B")
product_b.use() # 输出: 使用具体产品B
说明:
使用
globals()
动态获取类对象,根据传入的product_type
构建类名并实例化。这种方法减少了工厂类中硬编码的条件判断,但需确保类名与产品类型严格对应。
C# 使用反射机制实现¶
using System;
using System.Reflection;
// 产品接口
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 class SimpleFactory
{
public static IProduct CreateProduct(string type)
{
string className = $"ConcreteProduct{type.ToUpper()}";
Type typeObj = Type.GetType(className);
if (typeObj == null)
{
throw new ArgumentException("未知的产品类型: " + type);
}
return (IProduct)Activator.CreateInstance(typeObj);
}
}
// 客户端代码
public class Client
{
public static void Main(string[] args)
{
try
{
IProduct productA = SimpleFactory.CreateProduct("A");
productA.Use(); // 输出: 使用具体产品A
IProduct productB = SimpleFactory.CreateProduct("B");
productB.Use(); // 输出: 使用具体产品B
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
说明:
使用
Type.GetType
根据类名获取Type
对象。使用
Activator.CreateInstance
动态实例化对象。需要确保类的完全限定名(包括命名空间),否则
Type.GetType
可能返回null
。
6. 简单工厂模式的优缺点¶
优点:
封装对象创建:将对象的创建过程集中在工厂类中,客户端无需了解具体类的创建细节。
简化客户端代码:客户端通过工厂方法获取对象,减少了直接实例化的代码量。
集中管理对象创建:所有对象的创建由工厂类统一管理,便于维护和扩展。
符合开闭原则:通过修改工厂类,可以增加新产品的支持,而无需修改客户端代码。
缺点:
违反单一职责原则:工厂类承担了创建所有产品的职责,可能导致工厂类过于庞大。
增加系统复杂性:每增加一种产品类型,都需要修改工厂类,增加了工厂类的复杂性。
不易扩展:对于产品种类频繁变化的系统,简单工厂模式可能不够灵活,难以适应需求变化。
7. 简单工厂模式的常见误区与解决方案¶
误区1:简单工厂模式等同于工厂方法模式
事实:简单工厂模式通过一个工厂类创建所有产品,而工厂方法模式通过定义一个抽象工厂接口,让子类决定具体创建哪一种产品。
解决方案:理解两者的区别,简单工厂适用于产品种类不频繁变化的场景,工厂方法适用于产品种类频繁变化或需要扩展的场景。
误区2:简单工厂模式可以代替所有工厂模式
事实:简单工厂模式虽然简化了对象创建,但在复杂或需要扩展的场景下,工厂方法或抽象工厂模式更为适用。
解决方案:根据系统的复杂性和扩展需求选择合适的工厂模式,不要强行使用简单工厂模式解决所有问题。
误区3:简单工厂模式无需考虑并发问题
事实:在多线程环境下,工厂类的静态方法可能会导致线程安全问题,特别是在懒加载实例时。
解决方案:确保工厂方法的线程安全,可以通过同步机制或使用线程安全的数据结构来管理。
误区4:简单工厂模式可以解决所有对象创建的问题
事实:简单工厂模式主要解决对象创建的封装问题,对于对象生命周期管理、复杂依赖注入等问题,可能需要结合其他设计模式。
解决方案:根据具体需求,结合其他设计模式(如依赖注入、单例模式)来完善系统的对象创建和管理。
8. 简单工厂模式的扩展与变体¶
1. 静态工厂方法
定义:将工厂方法定义为静态方法,便于无需实例化工厂类即可调用创建方法。
应用场景:适用于工厂类不需要维护任何状态,仅提供对象创建功能的场景。
2. 动态工厂
定义:通过反射机制动态加载和实例化产品类,减少工厂类的硬编码依赖。
应用场景:需要根据配置或运行时参数动态选择产品类的场景,如插件系统。
3. 配置驱动的工厂
定义:通过外部配置(如XML、JSON文件)指定产品类的信息,工厂类根据配置动态创建对象。
应用场景:需要灵活配置产品类而不修改代码的场景,如依赖注入框架。
4. 简单工厂与其他设计模式结合
工厂方法模式:在简单工厂的基础上,通过抽象工厂接口和具体工厂子类实现更灵活的对象创建。
单例模式:将工厂类设计为单例,确保工厂类的唯一性,统一管理对象创建。
9. 简单工厂模式的应用实例¶
实例1:图形绘制应用中的图形对象创建
描述:
在一个图形绘制应用中,根据用户选择创建不同类型的图形对象(如圆形、矩形、三角形)。通过简单工厂模式,可以将图形对象的创建过程集中在工厂类中,简化客户端代码。
Java 实现
// 产品接口
public interface Shape {
void draw();
}
// 具体产品类 - 圆形
public class Circle implements Shape {
public void draw() {
System.out.println("绘制圆形");
}
}
// 具体产品类 - 矩形
public class Rectangle implements Shape {
public void draw() {
System.out.println("绘制矩形");
}
}
// 具体产品类 - 三角形
public class Triangle implements Shape {
public void draw() {
System.out.println("绘制三角形");
}
}
// 工厂类
public class ShapeFactory {
public static Shape createShape(String shapeType) {
if (shapeType == null) {
throw new IllegalArgumentException("形状类型不能为空");
}
switch (shapeType.toUpperCase()) {
case "CIRCLE":
return new Circle();
case "RECTANGLE":
return new Rectangle();
case "TRIANGLE":
return new Triangle();
default:
throw new IllegalArgumentException("未知的形状类型: " + shapeType);
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Shape shape1 = ShapeFactory.createShape("CIRCLE");
shape1.draw(); // 输出: 绘制圆形
Shape shape2 = ShapeFactory.createShape("RECTANGLE");
shape2.draw(); // 输出: 绘制矩形
Shape shape3 = ShapeFactory.createShape("TRIANGLE");
shape3.draw(); // 输出: 绘制三角形
}
}
输出:
绘制圆形
绘制矩形
绘制三角形
实例2:日志记录系统中的日志记录器创建
描述:
在一个日志记录系统中,根据不同的日志类型(如文件日志、数据库日志、控制台日志)创建相应的日志记录器。通过简单工厂模式,可以将日志记录器的创建过程集中在工厂类中,简化客户端代码。
Python 实现
from abc import ABC, abstractmethod
# 产品接口
class Logger(ABC):
@abstractmethod
def log(self, message: str):
pass
# 具体产品类 - 文件日志记录器
class FileLogger(Logger):
def log(self, message: str):
print(f"将日志写入文件: {message}")
# 具体产品类 - 数据库日志记录器
class DatabaseLogger(Logger):
def log(self, message: str):
print(f"将日志写入数据库: {message}")
# 具体产品类 - 控制台日志记录器
class ConsoleLogger(Logger):
def log(self, message: str):
print(f"在控制台输出日志: {message}")
# 工厂类
class LoggerFactory:
@staticmethod
def create_logger(logger_type: str) -> Logger:
if logger_type.upper() == "FILE":
return FileLogger()
elif logger_type.upper() == "DATABASE":
return DatabaseLogger()
elif logger_type.upper() == "CONSOLE":
return ConsoleLogger()
else:
raise ValueError(f"未知的日志记录器类型: {logger_type}")
# 客户端代码
if __name__ == "__main__":
factory = LoggerFactory()
logger1 = factory.create_logger("FILE")
logger1.log("这是文件日志") # 输出: 将日志写入文件: 这是文件日志
logger2 = factory.create_logger("DATABASE")
logger2.log("这是数据库日志") # 输出: 将日志写入数据库: 这是数据库日志
logger3 = factory.create_logger("CONSOLE")
logger3.log("这是控制台日志") # 输出: 在控制台输出日志: 这是控制台日志
输出:
将日志写入文件: 这是文件日志
将日志写入数据库: 这是数据库日志
在控制台输出日志: 这是控制台日志
实例3:交通工具创建工厂
描述:
在一个交通工具管理系统中,根据不同的交通工具类型(如汽车、自行车、摩托车)创建相应的交通工具对象。通过简单工厂模式,可以将交通工具的创建过程集中在工厂类中,简化客户端代码。
C# 实现
using System;
// 产品接口
public interface IVehicle
{
void Drive();
}
// 具体产品类 - 汽车
public class Car : IVehicle
{
public void Drive()
{
Console.WriteLine("驾驶汽车");
}
}
// 具体产品类 - 自行车
public class Bicycle : IVehicle
{
public void Drive()
{
Console.WriteLine("骑自行车");
}
}
// 具体产品类 - 摩托车
public class Motorcycle : IVehicle
{
public void Drive()
{
Console.WriteLine("驾驶摩托车");
}
}
// 工厂类
public class VehicleFactory
{
public static IVehicle CreateVehicle(string vehicleType)
{
if (string.IsNullOrEmpty(vehicleType))
{
throw new ArgumentException("交通工具类型不能为空");
}
switch (vehicleType.ToUpper())
{
case "CAR":
return new Car();
case "BICYCLE":
return new Bicycle();
case "MOTORCYCLE":
return new Motorcycle();
default:
throw new ArgumentException("未知的交通工具类型: " + vehicleType);
}
}
}
// 客户端代码
public class Client
{
public static void Main(string[] args)
{
IVehicle vehicle1 = VehicleFactory.CreateVehicle("Car");
vehicle1.Drive(); // 输出: 驾驶汽车
IVehicle vehicle2 = VehicleFactory.CreateVehicle("Bicycle");
vehicle2.Drive(); // 输出: 骑自行车
IVehicle vehicle3 = VehicleFactory.CreateVehicle("Motorcycle");
vehicle3.Drive(); // 输出: 驾驶摩托车
}
}
输出:
驾驶汽车
骑自行车
驾驶摩托车
10. 简单工厂模式的最佳实践¶
确保工厂类的单一职责:
工厂类只负责对象的创建,不承担其他业务逻辑,保持职责单一。
使用枚举或常量定义产品类型:
通过枚举或常量来定义产品类型,避免使用硬编码的字符串,减少出错可能。
处理未知的产品类型:
在工厂方法中处理未知的产品类型,提供适当的异常或默认行为,增强系统的健壮性。
封装工厂类:
将工厂类设计为独立的类,避免工厂方法与其他类混合,保持代码的清晰性和可维护性。
考虑线程安全:
在多线程环境下,确保工厂方法的线程安全,避免并发问题。
利用反射机制(根据语言特性):
在支持反射的语言中,可以利用反射动态加载和实例化产品类,减少工厂类的硬编码依赖。
文档与命名规范:
清晰地命名工厂类和工厂方法,确保代码的可读性和可维护性。
测试工厂类:
为工厂类编写单元测试,确保不同产品类型的创建逻辑正确无误。
结合其他设计模式:
根据需要,将简单工厂模式与其他设计模式(如单例模式、策略模式)结合使用,提升系统的设计能力。
限制工厂类的扩展:
如果工厂类变得过于复杂,可以考虑将其拆分为多个工厂类,或转而使用更灵活的工厂方法模式。
11. 总结¶
简单工厂模式是一种基础的创建型设计模式,通过集中管理对象的创建过程,封装了对象的实例化逻辑,简化了客户端代码。它适用于需要创建多种相似对象且对象创建逻辑相对简单的场景。然而,随着系统复杂性的增加,简单工厂模式可能面临扩展性不足的问题,此时可以考虑使用工厂方法模式或抽象工厂模式来替代。
关键学习点回顾:
理解简单工厂模式的核心概念:集中管理对象创建,通过工厂类创建不同类型的对象。
掌握不同的实现方式:基本实现、使用内置库特性实现等。
识别适用的应用场景:对象创建逻辑相对简单、多种相似对象创建、客户端不关心具体类等。
认识简单工厂模式的优缺点:封装对象创建、简化客户端代码 vs. 违反单一职责原则、增加系统复杂性、不易扩展。
避免常见误区:正确选择工厂模式的类型、结合其他设计模式、确保工厂方法的健壮性和灵活性。