什么是工厂模式
工厂模式是一种创建对象的设计模式,它提供了一种将对象的创建逻辑封装在一个工厂类中的方式。通过使用工厂模式,客户端代码不需要直接实例化对象,而是通过调用工厂类的方法来获取对象实例。
工厂模式的分类
工厂模式有多种变体,其中最常见的有三种:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
简单工厂模式
简单工厂模式通过一个工厂类来创建不同类型的对象。客户端只需要提供一个参数,工厂类就会根据这个参数来决定创建哪种类型的对象。
- 优点: 将对象的创建和使用分离,客户端只需要关心接口而不需要关心具体的对象创建过程。 可以通过工厂类来集中管理和控制对象的创建过程,方便进行统一的管理和维护。
- 缺点:当需要新增一种类型的对象时,需要修改工厂类的代码。
简单工厂模式由三个主要角色组成:
- 工厂类(Factory):负责创建具体对象的工厂类,通常包含一个静态方法或函数,根据传入的参数来创建不同类型的对象。
- 抽象产品类(Product):定义了具体产品类的公共接口,所有具体产品类都要实现这个接口。
- 具体产品类(Concrete Product):实现了抽象产品类定义的接口,是工厂类创建的目标对象。
简单工厂模式的核心思想是将对象的创建过程封装在工厂类中,客户端只需要通过工厂类的接口来创建对象,而不需要直接调用具体产品类的构造函数。
代码实例:
#include <iostream>
// 抽象产品类
class Product {
public:
virtual void use() = 0;
};
// 具体产品类A
class ConcreteProductA : public Product {
public:
void use() override {
std::cout << "Using ConcreteProductA" << std::endl;
}
};
// 具体产品类B
class ConcreteProductB : public Product {
public:
void use() override {
std::cout << "Using ConcreteProductB" << std::endl;
}
};
// 工厂类
class Factory {
public:
static Product* createProduct(int type) {
if (type == 1) {
return new ConcreteProductA();
} else if (type == 2) {
return new ConcreteProductB();
}
return nullptr;
}
};
int main() {
Product* productA = Factory::createProduct(1);
if (productA != nullptr) {
productA->use();
delete productA;
}
Product* productB = Factory::createProduct(2);
if (productB != nullptr) {
productB->use();
delete productB;
}
return 0;
}
在上面的示例中,我们定义了一个抽象产品类Product,并派生出两个具体产品类ConcreteProductA和ConcreteProductB,它们都实现了抽象产品类的接口。
然后我们定义了一个工厂类Factory,其中的静态方法createProduct根据传入的参数来创建不同类型的产品对象。
在客户端代码中,我们通过调用工厂类的接口来创建具体产品对象,并使用这些对象进行相应的操作。这样,客户端代码就不需要直接调用具体产品类的构造函数,而是通过工厂类来创建对象,实现了对象创建和使用的分离。
工厂方法模式
工厂方法模式的核心思想是通过将对象的创建委托给子类来实现多态性。每个具体的子类都实现了一个工厂方法,用于创建特定类型的对象。这样,当需要创建不同类型的对象时,只需要实例化相应的具体工厂类即可,而不需要修改现有的代码。
下面是一个示例代码,展示了如何使用工厂方法模式创建不同类型的日志记录器对象:
#include <iostream>
#include <string>
// 抽象产品接口
class Logger {
public:
virtual void log(const std::string& message) = 0;
};
// 具体产品类:文件日志记录器
class FileLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "File Logger: " << message << std::endl;
}
};
// 具体产品类:数据库日志记录器
class DatabaseLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "Database Logger: " << message << std::endl;
}
};
// 抽象工厂类
class LoggerFactory {
public:
virtual Logger* createLogger() = 0;
};
// 具体工厂类:文件日志记录器工厂
class FileLoggerFactory : public LoggerFactory {
public:
Logger* createLogger() override {
return new FileLogger();
}
};
// 具体工厂类:数据库日志记录器工厂
class DatabaseLoggerFactory : public LoggerFactory {
public:
Logger* createLogger() override {
return new DatabaseLogger();
}
};
int main() {
LoggerFactory* factory = new FileLoggerFactory();
Logger* logger = factory->createLogger();
logger->log("This is a file log message.");
delete logger;
delete factory;
factory = new DatabaseLoggerFactory();
logger = factory->createLogger();
logger->log("This is a database log message.");
delete logger;
delete factory;
return 0;
}
在上述示例中,我们定义了抽象产品接口 Logger 和具体产品类 FileLogger 和 DatabaseLogger。然后,我们定义了抽象工厂类 LoggerFactory 和具体工厂类 FileLoggerFactory 和 DatabaseLoggerFactory,每个具体工厂类都负责创建相应类型的产品对象。
在客户端代码中,我们首先创建一个具体工厂类的实例,然后使用工厂对象调用 createLogger 方法来获取相应类型的日志记录器对象。最后,我们使用日志记录器对象来记录日志消息。
工厂方法模式的优点包括将对象的创建与使用分离、封装了对象的创建逻辑、提供了灵活性和可扩展性等。它常用于需要根据不同的条件创建不同类型的对象的场景。
抽象工厂模式
它提供了一种将一组相关的工厂封装在一起的方式。每个具体的工厂类都负责创建一组相关的对象。客户端通过与抽象工厂接口进行交互,可以创建一组相关的对象,而无需关心具体的实现类。
抽象工厂模式通常涉及到多个产品族,每个产品族都有多个具体的产品类。抽象工厂接口定义了一组用于创建产品族的方法,每个具体的工厂类都实现了这些方法,用于创建相应的产品。
下面是一个示例代码,展示了如何使用抽象工厂模式创建不同类型的按钮和文本框对象:
#include <iostream>
#include <string>
// 抽象产品接口:按钮
class Button {
public:
virtual void paint() = 0;
};
// 具体产品类:Windows 按钮
class WindowsButton : public Button {
public:
void paint() override {
std::cout << "Paint a Windows button." << std::endl;
}
};
// 具体产品类:Mac 按钮
class MacButton : public Button {
public:
void paint() override {
std::cout << "Paint a Mac button." << std::endl;
}
};
// 抽象产品接口:文本框
class TextField {
public:
virtual void paint() = 0;
};
// 具体产品类:Windows 文本框
class WindowsTextField : public TextField {
public:
void paint() override {
std::cout << "Paint a Windows text field." << std::endl;
}
};
// 具体产品类:Mac 文本框
class MacTextField : public TextField {
public:
void paint() override {
std::cout << "Paint a Mac text field." << std::endl;
}
};
// 抽象工厂接口
class GUIFactory {
public:
virtual Button* createButton() = 0;
virtual TextField* createTextField() = 0;
};
// 具体工厂类:Windows 工厂
class WindowsFactory : public GUIFactory {
public:
Button* createButton() override {
return new WindowsButton();
}
TextField* createTextField() override {
return new WindowsTextField();
}
};
// 具体工厂类:Mac 工厂
class MacFactory : public GUIFactory {
public:
Button* createButton() override {
return new MacButton();
}
TextField* createTextField() override {
return new MacTextField();
}
};
int main() {
GUIFactory* factory = new WindowsFactory();
Button* button = factory->createButton();
TextField* textField = factory->createTextField();
button->paint();
textField->paint();
delete button;
delete textField;
delete factory;
factory = new MacFactory();
button = factory->createButton();
textField = factory->createTextField();
button->paint();
textField->paint();
delete button;
delete textField;
delete factory;
return 0;
}
在上述示例中,我们定义了抽象产品接口 Button 和 TextField,以及具体产品类 WindowsButton、MacButton、WindowsTextField 和 MacTextField。然后,我们定义了抽象工厂接口 GUIFactory,以及具体工厂类 WindowsFactory 和 MacFactory,每个具体工厂类都负责创建相应类型的产品对象。
在客户端代码中,我们首先创建一个具体工厂类的实例,然后使用工厂对象调用 createButton 和 createTextField 方法来获取相应类型的按钮和文本框对象。最后,我们使用这些对象来进行绘制操作。
抽象工厂模式的优点包括将对象的创建与使用分离、封装了对象的创建逻辑、提供了灵活性和可扩展性等。它常用于需要创建一组相关的对象,或者需要根据不同的条件创建不同类型的对象的场景。
总结
工厂模式的优点包括将对象的创建与使用分离、封装了对象的创建逻辑、提供了灵活性和可扩展性等。它常用于复杂对象的创建,或者在创建对象时需要进行一些额外的逻辑处理的情况下。