简单工厂模式(Simple Factory Pattern)是设计模式中的一种创建型模式。它的主要目的是将对象的实例化与使用解耦,使得客户端无需关心对象的创建细节,只需通过工厂类来获取所需的对象。
在简单工厂模式中,通常会有一个工厂类,它负责根据客户端的请求创建并返回相应的对象。客户端只需调用工厂类的方法并传入相应的参数,即可获取所需的对象,而无需关心对象的创建过程。
在简单工厂模式中,通常包括以下几个角色:
(1)工厂(Creator)角色: 工厂角色是简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。
(2)抽象产品(Product)角色: 抽象产品角色是简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
(3)具体产品(Concrete Product)角色: 具体产品角色是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
简单工厂模式允许客户端只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其创建的细节。这有利于明确各个类的职责,并优化整个软件体系结构。
在 C++ 实现简单工厂模式的实现步骤如下:
(1)定义产品接口: 首先,你需要定义一个抽象的产品接口,这个接口将声明所有产品对象都需要实现的方法。这确保了工厂方法能够返回统一类型的产品对象,而客户端则可以通过这个接口来操作这些对象。
(2)实现具体产品类: 接下来,创建实现了产品接口的具体产品类。这些类将实现接口中定义的方法,并且提供具体的功能。
(3)创建工厂类: 然后,你需要创建一个工厂类,这个类将负责根据客户端的请求创建并返回相应的产品对象。工厂类通常包含一个静态方法,这个方法接收一个参数(如产品类型或标识符),然后基于这个参数来实例化并返回相应的产品对象。
(4)客户端调用工厂方法: 客户端代码不需要直接实例化产品对象,而是调用工厂类的静态方法,并传入所需的参数。工厂方法根据这些参数创建并返回相应的产品对象。
(5)客户端使用产品对象: 客户端现在可以通过产品接口来使用返回的产品对象,而无需关心对象是如何创建的。
(6)处理资源释放: 由于工厂模式通常涉及到动态内存分配(例如使用 new 关键字创建对象),因此客户端在使用完产品对象后需要负责释放这些对象所占用的资源(如使用 delete 关键字)。这通常是客户端调用工厂方法后需要注意的一个方面。
如下为样例代码:
#include
#include
#include
// 产品抽象接口
class Product {
public:
virtual void use() = 0;
virtual ~Product() {} // 虚析构函数确保正确释放派生类对象
};
// 具体产品类A
class ProductA : public Product {
public:
void use() override {
std::cout << "Using ProductA" << std::endl;
}
};
// 具体产品类B
class ProductB : public Product {
public:
void use() override {
std::cout << "Using ProductB" << std::endl;
}
};
// 工厂类
class SimpleFactory {
public:
// 使用智能指针返回产品对象
static std::unique_ptr<Product> createProduct(const std::string& type) {
if (type == "A") {
return std::make_unique<ProductA>(); // 使用make_unique创建ProductA对象
}
else if (type == "B") {
return std::make_unique<ProductB>(); // 使用make_unique创建ProductB对象
}
else {
return nullptr; // 或者抛出一个异常
}
}
};
// 客户端代码
int main()
{
// 使用工厂方法创建产品对象,并自动管理其生命周期
auto productA = SimpleFactory::createProduct("A");
if (productA) {
productA->use();
// 不需要手动delete,unique_ptr会在离开作用域时自动释放内存
}
auto productB = SimpleFactory::createProduct("B");
if (productB) {
productB->use();
// 同样,不需要手动delete
}
return 0;
}
上面代码的输出为:
Using ProductA
Using ProductB
C++ 简单工厂模式的应用场景主要包括以下几种情况:
(1)创建对象不需要知道具体类的情况: 当客户端需要创建某个产品对象,但并不需要知道具体是哪个类的时候,可以使用简单工厂模式。工厂类根据客户端的请求和提供的参数,返回相应的产品对象,客户端只需要通过产品接口来操作这些对象。
(2)减少客户端与具体产品类的耦合: 在传统的设计中,客户端通常需要直接实例化具体的产品类。这会导致客户端与产品类紧密耦合,不利于代码的维护和扩展。通过使用简单工厂模式,客户端只需与工厂类交互,从而降低了与具体产品类的耦合度。
(3)需要创建的对象较少且不会经常变动: 简单工厂模式适用于产品类型相对较少且不太可能经常变动的场景。如果产品类型非常多或者经常需要添加新的产品类型,简单工厂模式可能会导致工厂类变得庞大而难以维护。在这种情况下,可能需要考虑使用其他更高级的工厂模式,如工厂方法模式或抽象工厂模式。
(4)隐藏产品类的具体实现细节: 简单工厂模式可以隐藏产品类的具体实现细节,使得客户端无需关心产品对象的创建过程。这有助于将对象的创建与使用分离,提高代码的可读性和可维护性。
(5)统一创建接口: 当多个产品类具有相似的创建逻辑时,可以使用简单工厂模式来统一这些产品的创建接口。这样,客户端可以通过统一的接口来创建不同类型的产品对象,简化了代码结构。
以下是一个简单工厂模式应用于创建对象而不需要知道具体类的样例。在这个例子中,有一个 Shape 接口和两个实现了这个接口的类:Circle 和 Rectangle。客户端代码通过简单工厂类 ShapeFactory 来创建这些形状对象,而无需知道它们的具体类。
#include
#include
#include // 用于std::unique_ptr
// 形状接口
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {} // 虚析构函数确保正确释放派生类对象
};
// 圆形类
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
// 矩形类
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a rectangle." << std::endl;
}
};
// 形状工厂类
class ShapeFactory {
public:
// 根据类型创建形状对象
static std::unique_ptr<Shape> createShape(const std::string& shapeType) {
if (shapeType == "Circle") {
return std::make_unique<Circle>();
}
else if (shapeType == "Rectangle") {
return std::make_unique<Rectangle>();
}
else {
return nullptr; // 或者抛出一个异常
}
}
};
// 客户端代码
int main()
{
// 使用工厂创建形状对象,无需知道具体类
std::unique_ptr<Shape> shape1 = ShapeFactory::createShape("Circle");
if (shape1) {
shape1->draw(); // 输出:Drawing a circle.
}
std::unique_ptr<Shape> shape2 = ShapeFactory::createShape("Rectangle");
if (shape2) {
shape2->draw(); // 输出:Drawing a rectangle.
}
// 无需担心shape1和shape2的具体类型,它们都被当作Shape接口使用
return 0;
}
上面代码的输出为:
Drawing a circle.
Drawing a rectangle.
在这个例子中,客户端代码通过调用 ShapeFactory::createShape 方法并传入形状类型(“Circle"或"Rectangle”)来创建形状对象。由于返回的是 Shape 接口的 unique_ptr,客户端代码可以统一地调用 draw 方法,而无需关心实际创建的是哪种形状对象。这种方式使得客户端代码更加灵活,并且容易扩展新的形状类型,因为只需要在工厂类中添加新的创建逻辑即可。
以下是一个简单工厂模式应用于减少客户端与具体产品类耦合的 C++ 样例。在这个例子中,有一个 Payment 接口和两个实现了该接口的类:CreditCardPayment 和 CashPayment。客户端代码通过 PaymentFactory 简单工厂类来创建支付对象,从而减少了与具体支付类的耦合。
#include
#include
#include // 用于std::unique_ptr
// 支付接口
class Payment {
public:
virtual void pay() = 0;
virtual ~Payment() {} // 虚析构函数确保正确释放派生类对象
};
// 信用卡支付类
class CreditCardPayment : public Payment {
public:
void pay() override {
std::cout << "Processing credit card payment." << std::endl;
}
};
// 现金支付类
class CashPayment : public Payment {
public:
void pay() override {
std::cout << "Processing cash payment." << std::endl;
}
};
// 支付工厂类
class PaymentFactory {
public:
// 根据支付方式创建支付对象
static std::unique_ptr<Payment> createPayment(const std::string& paymentType) {
if (paymentType == "CreditCard") {
return std::make_unique<CreditCardPayment>();
}
else if (paymentType == "Cash") {
return std::make_unique<CashPayment>();
}
else {
return nullptr; // 或者抛出一个异常
}
}
};
// 客户端代码
int main()
{
// 使用工厂创建支付对象,减少与具体支付类的耦合
std::unique_ptr<Payment> payment1 = PaymentFactory::createPayment("CreditCard");
if (payment1) {
payment1->pay(); // 输出:Processing credit card payment.
}
std::unique_ptr<Payment> payment2 = PaymentFactory::createPayment("Cash");
if (payment2) {
payment2->pay(); // 输出:Processing cash payment.
}
// 客户端代码不需要直接实例化CreditCardPayment或CashPayment,只需要通过Payment接口和工厂类进行交互
return 0;
}
上面代码的输出为:
Processing credit card payment.
Processing cash payment.
在这个例子中,客户端代码不需要知道 CreditCardPayment 或 CashPayment 类的具体实现细节。它只需要调用 PaymentFactory::createPayment 方法,并传入支付方式(“CreditCard” 或 “Cash”),工厂类就会返回相应的支付对象。由于返回的是 Payment 接口的 unique_ptr,客户端代码可以统一地调用 pay 方法,而无需关心实际创建的是哪种支付对象。
这种方式减少了客户端与具体支付类的耦合,使得客户端代码更加简洁和易于维护。如果需要添加新的支付方式,只需要在工厂类中添加新的创建逻辑,而无需修改客户端代码。这符合开闭原则,即对扩展开放,对修改封闭。
C++ 简单工厂模式的优点主要包括:
(1)封装性好: 简单工厂模式封装了对象创建的细节,客户端不需要直接实例化具体的产品类。这有助于隐藏具体产品类的实现细节,使得客户端代码更加简洁和易于维护。
(2)解耦: 简单工厂模式降低了客户端与具体产品类之间的耦合度。客户端只需要与工厂类交互,而不需要知道具体产品类的实现细节。这有助于减少代码的依赖性和提高系统的可维护性。
(3)代码复用: 工厂类集中了产品对象的创建逻辑,可以在多个地方重用该工厂类来创建对象,提高了代码的复用性。
(4)扩展性好: 当需要添加新的产品类时,只需要在工厂类中添加相应的创建逻辑,而无需修改客户端代码。这符合开闭原则,即对扩展开放,对修改封闭。
然而,C++ 简单工厂模式也存在一些缺点:
(1)违反单一职责原则: 简单工厂类通常负责创建多种类型的产品对象,这可能导致工厂类的职责过多,违反了单一职责原则。当产品类型较多时,工厂类的代码可能会变得庞大而难以维护。
(2)类型判断逻辑复杂: 简单工厂模式通常需要根据客户端提供的参数或类型信息来判断需要创建哪种类型的产品对象。随着产品类型的增加,类型判断逻辑可能会变得复杂和易错。
(3)不易于扩展新的产品族: 简单工厂模式是针对一个产品族设计的,如果需要添加新的产品族(即一组具有共同主题的产品),可能需要修改工厂类的代码,这违反了开闭原则。
(4)不易于使用继承等面向对象特性: 由于简单工厂模式通常直接返回具体产品类的实例,而不是通过继承等面向对象特性来创建对象,因此可能无法充分利用面向对象编程的优势。
需要注意的是,简单工厂模式是一种相对简单的工厂模式,适用于产品类型较少且不太可能经常变动的场景。在更复杂的场景中,可能需要考虑使用抽象工厂模式来应对更多的变化和扩展需求。