工厂模式

Raven005 Lv3

简介

在软件开发中,工厂模式是一种设计模式,主要用于创建对象而不暴露具体类的创建细节。这种模式的主要目标是封装创建过程,并将创建逻辑从客户端代码中解耦出来。

定义和意图:

工厂模式的意图在于提供一个创建一系列相关或相互依赖的对象的过程,而无需指定这些对象的具体类。它通过定义一个工厂类来实现这一功能,该工厂类负责根据输入参数决定创建哪个具体类的实例。

常见形式

工厂模式通常有以下几种常见形式:

  1. 简单工厂(Simple Factory)
    这是最基础的形式,它不依赖于类层次结构,并且通常被用于静态语言中。工厂只创建一个类族中的对象,通过传入参数来决定创建哪个具体类。

  2. 抽象工厂(Abstract Factory)
    抽象工厂模式提供了一系列的工厂方法,用于创建一组相关的产品对象。这些产品可以属于不同的家族,但必须遵循共同的接口或抽象定义。其目的是在多个产品的上下文之间进行隔离,使得客户端代码只需要知道如何使用产品而不是如何创建它们。

  3. 单例工厂(Singleton Factory)
    在此模式中,工厂类是单例的,确保了在整个应用范围内只有一个实例存在,并且提供一个公共的方法来访问这个实例。这是一种常见的设计模式,用于限制资源访问或配置设置。

  4. 构造函数工厂(Constructor Factory)
    这种形式在面向对象语言中较为常见,它通过构造方法创建和初始化对象。构造函数工厂可以接受参数构建具有特定配置的对象,提供了一种动态调整对象属性的机制。

使用场景

  • 在需要根据环境或运行时条件选择具体类时。
  • 当客户端代码不应该依赖于具体的类实现细节,而是应该通过抽象接口进行交互时。
  • 当创建复杂对象需要多个步骤、参数和依赖关系时(例如,配置文件中的信息用于决定对象实例的创建)。

示例

假设我们有一个简单工厂模式的例子。假设我们要根据用户输入创建不同类型的形状:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <memory>
#include <iostream>


// Abstract product
class Shape {
public:
Shape() = default;
virtual ~Shape() = default;
virtual void draw() = 0;
};
// Concrete Product
class Circle : public Shape {
public:
virtual void draw() override {
std::cout << "this is a circle" << std::endl;
}
};

class Square : public Shape {
public:
virtual void draw() override {
std::cout << "this is a square" << std::endl;
}
};

// Abstract Creator

class ShapeFactory {
public:
virtual std::shared_ptr<Shape> createShape() = 0;
virtual ~ShapeFactory() = default;
};

class CircleFactory : public ShapeFactory {
public:
std::shared_ptr<Shape> createShape() override {
return std::make_shared<Circle>();
}
};

class SquareFactory : public ShapeFactory {
public:
virtual std::shared_ptr<Shape> createShape() override {
return std::make_shared<Square>();
}
};
// Client
void test_func() {
std::unique_ptr<CircleFactory> circleFactory = std::make_unique<CircleFactory>();
std::unique_ptr<SquareFactory> squareFactory = std::make_unique<SquareFactory>();
auto circle = circleFactory->createShape();
auto square = squareFactory->createShape();
circle->draw();
square->draw();
}

在这个例子中,ShapeShapeFactory 是接口和抽象类的例子。工厂模式通过创建具体实现的实例(即 CircleSquare)来满足客户端的不同需求,而不需要在代码中硬编码具体的类名。

具体分析:

  1. 接口(Abstract Class)

    • Shape:这是抽象产品类,定义了一个纯虚函数draw(),用于绘制形状。
    • CircleFactory, SquareFactory:这些是抽象创建者类的实现。它们都实现了从抽象创建者类继承的createShape()接口方法。
  2. 具体类

    • CircleSquare 是具体产品类。它们各自实现了draw()函数以表示不同形状的绘制方式。
  3. 工厂模式

    • 在这个例子中,通过使用抽象创建者(ShapeFactory)和具体创建者(CircleFactory, SquareFactory),我们可以在不直接引用具体产品类的情况下创建产品对象。这种方法使得客户端代码与具体的产品实现解耦。
  4. 测试函数

    • test_func() 函数展示了如何通过工厂模式实例化不同的产品。它首先创建了CircleFactorySquareFactory的实例,然后调用每个工厂的createShape()方法来获取相应的形状对象,并调用它们的draw()方法来验证是否正确创建并绘制出了圆形和正方形。

使用场景:

  • 当需要在运行时决定使用哪个具体产品类时。
  • 需要将产品的创建过程与产品的客户端代码分离,以提高代码可维护性和灵活性。
  • 避免类的爆炸问题(即大量的相似类),通过一个工厂类来管理类的选择和实例化。

通过这种方式,工厂模式提供了一种灵活的方式来根据特定需求动态地生成不同类型的对象。在实际开发中,这种设计模式常用于UI组件、数据库连接器等场景,以提高代码的可扩展性和维护性。

总结

工厂模式为创建对象提供了极大的灵活性和封装性,并有助于降低耦合度和提高代码的可维护性。它通过提供一个统一的方式来实例化特定类型的对象或一组相关的对象,使得系统结构更加清晰、更易于调整和扩展。

工厂模式的缺点

虽然工厂模式在对象创建时提供了便利性和灵活性,但它同样存在一些潜在的局限和问题:

  1. 代码复杂性:随着功能的增加或需求的变化,工厂类可能会变得非常大且难以维护。每个产品都需要对应的创建方法或工厂实例,这可能导致代码结构混乱,尤其是在有大量相关产品的大型项目中。

  2. 单一职责原则违背:在某些情况下,一个工厂类可能需要处理多个不同类的创建问题,这违反了单一职责原则(SRP)。理想的工厂应该只负责创建特定类型的产品,如果必须同时处理多种类型,则其职责会变得过于广泛。

  3. 依赖增加:使用工厂模式引入了一个新的接口或抽象层,这可能导致代码中对工厂类的依赖增加。在某些情况下,这种依赖可能难以去除,尤其是当工厂类成为多个客户端组件之间的耦合点时。

  4. 动态性限制:虽然工厂模式允许通过传递参数或配置文件来控制创建过程中的决策(如产品实例的选择),但这些设置往往需要在代码中硬编码。如果需要在运行时进行更复杂的自定义逻辑,可能需要更复杂的方法或额外的工厂类,这会增加系统的复杂性。

  5. 调用链复杂:当一个工厂负责多个层次的对象创建时(例如,父工厂创建子工厂),可能会形成复杂的调用链。这种结构可能难以理解、测试和维护,尤其是在对象树非常深的情况下。

  6. 资源管理问题:在处理大量或动态创建的对象时,工厂模式可能导致资源管理问题,如内存泄露或未正确关闭的资源(例如数据库连接)。如果产品实例需要特定生命周期管理,那么必须仔细考虑如何在工厂中实现这一点,以避免这些问题。

  7. 非确定性创建过程:如果产品的创建过程依赖于外部因素(比如用户输入、配置文件、环境变量等),则工厂模式可能难以保证创建过程的确定性和可预测性。这可能会导致在某些情况下(如自动化测试)出现预期之外的结果或错误。

  8. 对需求变化的适应性差:如果产品的需求或类型经常发生变化,那么需要频繁地修改工厂类以支持这些新需求。这可能导致代码库中大量重复的工作和维护负担。