简单工厂模式

简单工厂模式作为工厂模式的一种变体,提供了一种通过工厂类创建对象的机制,从而封装对象的创建过程,简化客户端代码。


目录

  1. 什么是简单工厂模式?

  2. 简单工厂模式的意图

  3. 简单工厂模式的适用场景

  4. 简单工厂模式的结构

  5. 简单工厂模式的实现

  6. 简单工厂模式的优缺点

  7. 简单工厂模式的常见误区与解决方案

  8. 简单工厂模式的扩展与变体

  9. 简单工厂模式的应用实例

  10. 简单工厂模式的最佳实践

  11. 总结

  12. 参考资料


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. 简单工厂模式的优缺点

优点:

  1. 封装对象创建:将对象的创建过程集中在工厂类中,客户端无需了解具体类的创建细节。

  2. 简化客户端代码:客户端通过工厂方法获取对象,减少了直接实例化的代码量。

  3. 集中管理对象创建:所有对象的创建由工厂类统一管理,便于维护和扩展。

  4. 符合开闭原则:通过修改工厂类,可以增加新产品的支持,而无需修改客户端代码。

缺点:

  1. 违反单一职责原则:工厂类承担了创建所有产品的职责,可能导致工厂类过于庞大。

  2. 增加系统复杂性:每增加一种产品类型,都需要修改工厂类,增加了工厂类的复杂性。

  3. 不易扩展:对于产品种类频繁变化的系统,简单工厂模式可能不够灵活,难以适应需求变化。


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(); // 输出: 驾驶摩托车
    }
}

输出:

驾驶汽车
骑自行车
驾驶摩托车

6. 简单工厂模式的优缺点

优点:

  1. 封装对象创建:将对象的创建过程集中在工厂类中,客户端无需了解具体类的创建细节。

  2. 简化客户端代码:客户端通过工厂方法获取对象,减少了直接实例化的代码量。

  3. 集中管理对象创建:所有对象的创建由工厂类统一管理,便于维护和扩展。

  4. 符合开闭原则:通过修改工厂类,可以增加新产品的支持,而无需修改客户端代码。

缺点:

  1. 违反单一职责原则:工厂类承担了创建所有产品的职责,可能导致工厂类过于庞大。

  2. 增加系统复杂性:每增加一种产品类型,都需要修改工厂类,增加了工厂类的复杂性。

  3. 不易扩展:对于产品种类频繁变化的系统,简单工厂模式可能不够灵活,难以适应需求变化。


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. 简单工厂模式的最佳实践

  1. 确保工厂类的单一职责

    • 工厂类只负责对象的创建,不承担其他业务逻辑,保持职责单一。

  2. 使用枚举或常量定义产品类型

    • 通过枚举或常量来定义产品类型,避免使用硬编码的字符串,减少出错可能。

  3. 处理未知的产品类型

    • 在工厂方法中处理未知的产品类型,提供适当的异常或默认行为,增强系统的健壮性。

  4. 封装工厂类

    • 将工厂类设计为独立的类,避免工厂方法与其他类混合,保持代码的清晰性和可维护性。

  5. 考虑线程安全

    • 在多线程环境下,确保工厂方法的线程安全,避免并发问题。

  6. 利用反射机制(根据语言特性):

    • 在支持反射的语言中,可以利用反射动态加载和实例化产品类,减少工厂类的硬编码依赖。

  7. 文档与命名规范

    • 清晰地命名工厂类和工厂方法,确保代码的可读性和可维护性。

  8. 测试工厂类

    • 为工厂类编写单元测试,确保不同产品类型的创建逻辑正确无误。

  9. 结合其他设计模式

    • 根据需要,将简单工厂模式与其他设计模式(如单例模式、策略模式)结合使用,提升系统的设计能力。

  10. 限制工厂类的扩展

    • 如果工厂类变得过于复杂,可以考虑将其拆分为多个工厂类,或转而使用更灵活的工厂方法模式。


11. 总结

简单工厂模式是一种基础的创建型设计模式,通过集中管理对象的创建过程,封装了对象的实例化逻辑,简化了客户端代码。它适用于需要创建多种相似对象且对象创建逻辑相对简单的场景。然而,随着系统复杂性的增加,简单工厂模式可能面临扩展性不足的问题,此时可以考虑使用工厂方法模式或抽象工厂模式来替代。

关键学习点回顾:

  1. 理解简单工厂模式的核心概念:集中管理对象创建,通过工厂类创建不同类型的对象。

  2. 掌握不同的实现方式:基本实现、使用内置库特性实现等。

  3. 识别适用的应用场景:对象创建逻辑相对简单、多种相似对象创建、客户端不关心具体类等。

  4. 认识简单工厂模式的优缺点:封装对象创建、简化客户端代码 vs. 违反单一职责原则、增加系统复杂性、不易扩展。

  5. 避免常见误区:正确选择工厂模式的类型、结合其他设计模式、确保工厂方法的健壮性和灵活性。


12. 参考资料