工厂模式是设计模式中创建型模式的重要分支,在C++开发里,简单工厂作为工厂模式的基础实现形式,核心思路是把对象的创建逻辑封装到一个独立的工厂类中,让使用者不需要关心具体对象的实例化细节,只需要传入对应的参数就能获取到需要的对象实例。这种方式很适合对象创建逻辑相对简单、类型不会频繁变动的场景,能有效减少代码中的重复创建逻辑,降低模块之间的耦合程度。
简单工厂的核心组成
一个完整的C++简单工厂实现通常包含三个部分:
- 抽象产品类:定义所有具体产品需要实现的公共接口,通常是纯虚类,只声明公共方法不实现具体逻辑。
- 具体产品类:继承抽象产品类,实现各自的业务逻辑,是最终要创建的对象类型。
- 工厂类:包含静态的创建方法,根据传入的参数判断需要创建的具体产品类型,返回对应的对象指针。
C++简单工厂实现示例
下面以常见的图形绘制场景为例,实现简单工厂模式。我们需要创建圆形、矩形两种图形对象,它们都有绘制的方法,通过工厂类根据传入的类型参数创建对应的图形实例。
1. 定义抽象产品类
首先定义抽象的图形类,声明公共的绘制接口:
// 抽象产品类:图形基类
class Shape {
public:
// 纯虚函数,定义绘制的公共接口
virtual void draw() = 0;
// 虚析构函数,保证delete基类指针时能正确释放子类对象
virtual ~Shape() {}
};
2. 定义具体产品类
继承Shape类实现圆形和矩形两个具体产品类:
// 具体产品类:圆形
class Circle : public Shape {
public:
void draw() override {
// 圆形绘制逻辑
printf("绘制圆形n");
}
};
// 具体产品类:矩形
class Rectangle : public Shape {
public:
void draw() override {
// 矩形绘制逻辑
printf("绘制矩形n");
}
};
3. 实现简单工厂类
工厂类提供静态创建方法,根据传入的类型标识返回对应的图形对象:
// 简单工厂类
class ShapeFactory {
public:
// 静态创建方法,根据类型返回对应的图形对象
static Shape* createShape(const char* type) {
if (strcmp(type, "circle") == 0) {
return new Circle();
} else if (strcmp(type, "rectangle") == 0) {
return new Rectangle();
} else {
// 未知类型返回空指针
return nullptr;
}
}
};
4. 使用示例
在使用时不需要直接实例化Circle或Rectangle,只需要调用工厂的创建方法即可:
#include <cstdio>
#include <cstring>
int main() {
// 通过工厂创建圆形对象
Shape* circle = ShapeFactory::createShape("circle");
if (circle != nullptr) {
circle->draw();
delete circle;
circle = nullptr;
}
// 通过工厂创建矩形对象
Shape* rectangle = ShapeFactory::createShape("rectangle");
if (rectangle != nullptr) {
rectangle->draw();
delete rectangle;
rectangle = nullptr;
}
// 尝试创建未知类型
Shape* unknown = ShapeFactory::createShape("triangle");
if (unknown == nullptr) {
printf("未知图形类型,创建失败n");
}
return 0;
}
简单工厂的优缺点
简单工厂的优势非常明显,首先是封装了对象创建逻辑,使用者不需要知道具体产品的类名和实例化细节,只需要和工厂类交互即可;其次是减少了重复代码,如果多个地方需要创建同类型的对象,只需要调用工厂方法,不需要重复写实例化逻辑。但它也有局限性,当需要新增产品类型时,必须修改工厂类的创建方法,违反了开闭原则,所以更适合产品类型相对固定、不会频繁扩展的场景。
注意事项
在C++中使用简单工厂时,需要注意内存管理问题,因为工厂返回的是堆上创建的对象指针,使用者需要负责在不需要的时候释放内存,避免内存泄漏。如果担心内存管理问题,也可以结合智能指针使用,让工厂返回std::unique_ptr<Shape>类型的对象,自动管理生命周期。另外,工厂类的创建方法通常设计为静态方法,不需要实例化工厂类就可以调用,使用起来更加方便。