在C++开发中,抽象工厂模式(Abstract Factory Pattern)是解决"产品族创建"问题的利器。我曾在多个跨平台项目中应用这个模式,它最显著的优势在于:当系统需要支持多套相互关联的产品实现时,能够保持代码的整洁性和可维护性。
产品族指的是一组具有共同主题或风格的相关产品。比如:
这些产品需要协同工作,保持视觉和行为的一致性。如果直接在代码中混用不同风格的产品,会导致界面混乱和用户体验不一致。
在未使用抽象工厂模式前,常见的创建方式是这样的:
cpp复制// 不好的实践:直接创建具体产品
Button* btn = new WindowsButton();
Checkbox* cb = new LinuxCheckbox(); // 风格混搭!
这种方式存在三个主要问题:
抽象工厂模式通过以下方式解决这些问题:
这样切换产品族时,只需更换工厂实例即可:
cpp复制// 好的实践:通过抽象工厂创建
GUIFactory* factory = new WindowsFactory(); // 只需改这一处
Button* btn = factory->createButton(); // 自动创建匹配风格的产品
Checkbox* cb = factory->createCheckbox();
下面我将详细拆解一个跨平台GUI组件的实现,这是我在实际项目中最常用的抽象工厂应用场景。
标准的抽象工厂实现包含以下核心组件:
code复制AbstractFactory/
├── AbstractProducts/ (抽象产品接口)
│ ├── Button.h
│ └── Checkbox.h
├── ConcreteProducts/ (具体产品实现)
│ ├── Windows/
│ │ ├── WindowsButton.h
│ │ └── WindowsCheckbox.h
│ └── Linux/
│ ├── LinuxButton.h
│ └── LinuxCheckbox.h
├── Factories/ (工厂类)
│ ├── GUIFactory.h (抽象工厂)
│ ├── WindowsFactory.h
│ └── LinuxFactory.h
└── main.cpp (客户端代码)
首先定义产品接口,这是保证不同产品族能互换的关键:
cpp复制// Button.h
class Button {
public:
virtual ~Button() {}
virtual void render() const = 0;
virtual void onClick() const = 0;
};
// Checkbox.h
class Checkbox {
public:
virtual ~Checkbox() {}
virtual void render() const = 0;
virtual void onToggle() const = 0;
};
提示:产品接口应该足够抽象,只定义通用行为,不包含平台特定细节。
以Windows平台为例,实现具体产品:
cpp复制// WindowsButton.h
class WindowsButton : public Button {
public:
void render() const override {
std::cout << "Windows风格按钮 [ ]" << std::endl;
}
void onClick() const override {
std::cout << "Windows按钮点击效果" << std::endl;
}
};
// WindowsCheckbox.h
class WindowsCheckbox : public Checkbox {
public:
void render() const override {
std::cout << "Windows复选框 [ ]" << std::endl;
}
void onToggle() const override {
std::cout << "Windows复选框切换音效" << std::endl;
}
};
Linux平台的实现类似,只是具体行为不同。这种对称结构是抽象工厂的典型特征。
定义创建产品的抽象接口:
cpp复制// GUIFactory.h
class GUIFactory {
public:
virtual ~GUIFactory() {}
virtual Button* createButton() const = 0;
virtual Checkbox* createCheckbox() const = 0;
// 可以扩展更多产品创建方法
};
每个产品族对应一个具体工厂:
cpp复制// WindowsFactory.h
class WindowsFactory : public GUIFactory {
public:
Button* createButton() const override {
return new WindowsButton();
}
Checkbox* createCheckbox() const override {
return new WindowsCheckbox();
}
};
// LinuxFactory.h
class LinuxFactory : public GUIFactory {
public:
Button* createButton() const override {
return new LinuxButton();
}
Checkbox* createCheckbox() const override {
return new LinuxCheckbox();
}
};
客户端代码只依赖抽象接口:
cpp复制#include "GUIFactory.h"
#include "WindowsFactory.h"
#include "LinuxFactory.h"
void createUI(const GUIFactory& factory) {
Button* btn = factory.createButton();
Checkbox* cb = factory.createCheckbox();
btn->render();
cb->render();
delete btn;
delete cb;
}
int main() {
// 根据配置决定使用哪个产品族
bool useWindows = true;
if (useWindows) {
WindowsFactory factory;
createUI(factory);
} else {
LinuxFactory factory;
createUI(factory);
}
return 0;
}
在实际项目中应用抽象工厂模式时,有几个关键点需要特别注意。
原始指针管理容易导致内存泄漏,更安全的方式是使用智能指针:
cpp复制// 修改工厂接口
std::unique_ptr<Button> createButton() const;
// 具体工厂实现
std::unique_ptr<Button> WindowsFactory::createButton() const {
return std::make_unique<WindowsButton>();
}
// 客户端使用
auto btn = factory.createButton(); // 自动管理生命周期
通常不需要每次都new工厂实例,可以考虑:
cpp复制class WindowsFactory : public GUIFactory {
public:
static WindowsFactory& instance() {
static WindowsFactory inst;
return inst;
}
// ...
};
// 使用
auto& factory = WindowsFactory::instance();
cpp复制std::map<std::string, std::unique_ptr<GUIFactory>> factoryCache;
GUIFactory* getFactory(const std::string& type) {
if (!factoryCache.count(type)) {
if (type == "windows") {
factoryCache[type] = std::make_unique<WindowsFactory>();
} else if (type == "linux") {
factoryCache[type] = std::make_unique<LinuxFactory>();
}
}
return factoryCache[type].get();
}
添加新产品族只需三步:
不需要修改任何现有代码,符合开闭原则。
有时产品之间需要相互引用,可以通过工厂注入:
cpp复制class AdvancedButton : public Button {
Checkbox* linkedCheckbox_;
public:
AdvancedButton(Checkbox* cb) : linkedCheckbox_(cb) {}
// ...
};
class AdvancedFactory : public GUIFactory {
public:
Button* createButton() const override {
return new AdvancedButton(createCheckbox());
}
// ...
};
在实际开发中,会遇到一些典型问题,以下是解决方案。
如果想添加一个新产品类型(如TextBox),需要:
这会违反开闭原则。解决方案:
cpp复制template <typename T>
class GenericFactory : public GUIFactory {
public:
Button* createButton() const { return new T::Button; }
Checkbox* createCheckbox() const { return new T::Checkbox; }
};
struct WindowsTraits {
using Button = WindowsButton;
using Checkbox = WindowsCheckbox;
};
using WindowsFactory = GenericFactory<WindowsTraits>;
如果需要运行时动态切换产品族,可以结合抽象工厂和注册模式:
cpp复制class FactoryRegistry {
std::map<std::string, std::function<GUIFactory*()>> creators_;
public:
void registerFactory(const std::string& name, auto creator) {
creators_[name] = creator;
}
GUIFactory* createFactory(const std::string& name) const {
return creators_.at(name)();
}
};
// 注册工厂
FactoryRegistry registry;
registry.registerFactory("windows", [] { return new WindowsFactory; });
registry.registerFactory("linux", [] { return new LinuxFactory; });
// 根据配置创建
auto factory = registry.createFactory(config.platform);
当工厂和产品分布在不同的动态库中时,需要注意:
cpp复制// 模块入口点
extern "C" {
GUIFactory* createFactory() {
return new WindowsFactory;
}
void destroyFactory(GUIFactory* f) {
delete f;
}
}
虽然抽象工厂提供了良好的设计结构,但在性能敏感场景需要考虑以下优化。
频繁创建/销毁对象可能影响性能,解决方案:
cpp复制class ButtonPool {
std::vector<std::unique_ptr<Button>> pool_;
public:
Button* acquire() {
if (pool_.empty()) {
return factory_.createButton();
}
auto ptr = std::move(pool_.back());
pool_.pop_back();
return ptr.release();
}
void release(Button* btn) {
pool_.emplace_back(btn);
}
};
如果产品族在编译时已知,可以使用模板替代虚函数:
cpp复制template <typename Factory>
void renderUI() {
auto btn = Factory::createButton();
auto cb = Factory::createCheckbox();
// ...
}
// 使用
renderUI<WindowsFactory>();
这种方法消除了运行时多态开销,但失去了动态切换的能力。
虚函数调用可能导致缓存不友好,对于性能关键路径:
cpp复制template <typename Derived>
class ButtonBase : public Button {
public:
void render() const override {
static_cast<const Derived*>(this)->renderImpl();
}
};
class WindowsButton : public ButtonBase<WindowsButton> {
public:
void renderImpl() const { /* 具体实现 */ }
};
良好的测试是保证抽象工厂正确性的关键。我通常采用以下测试方法:
确保同一工厂创建的产品确实属于同一家族:
cpp复制TEST(WindowsFactoryTest, CreatesConsistentProducts) {
WindowsFactory factory;
auto btn = factory.createButton();
auto cb = factory.createCheckbox();
EXPECT_EQ(typeid(*btn), typeid(WindowsButton));
EXPECT_EQ(typeid(*cb), typeid(WindowsCheckbox));
delete btn;
delete cb;
}
验证不同产品族的行为差异:
cpp复制TEST(ButtonBehaviorTest, PlatformSpecificRendering) {
WindowsFactory winFactory;
LinuxFactory linuxFactory;
auto winBtn = winFactory.createButton();
auto linuxBtn = linuxFactory.createButton();
testing::internal::CaptureStdout();
winBtn->render();
std::string winOutput = testing::internal::GetCapturedStdout();
testing::internal::CaptureStdout();
linuxBtn->render();
std::string linuxOutput = testing::internal::GetCapturedStdout();
EXPECT_NE(winOutput, linuxOutput);
delete winBtn;
delete linuxBtn;
}
使用内存检测工具确保工厂正确释放资源:
cpp复制TEST(FactoryMemoryTest, NoMemoryLeak) {
{
WindowsFactory factory;
auto btn = factory.createButton();
auto cb = factory.createCheckbox();
delete btn;
delete cb;
} // 工厂析构后应无泄漏
}
在多年的C++开发中,我总结了以下抽象工厂的最佳实践:
适合场景:
不适合场景:
cpp复制class DialogBuilder {
GUIFactory& factory_;
public:
DialogBuilder(GUIFactory& f) : factory_(f) {}
Dialog build() {
Dialog d;
d.setButton(factory_.createButton());
d.setCheckbox(factory_.createCheckbox());
return d;
}
};
cpp复制class PrototypeFactory : public GUIFactory {
Button* buttonPrototype_;
Checkbox* checkboxPrototype_;
public:
Button* createButton() const override {
return buttonPrototype_->clone();
}
// ...
};
坑1:循环依赖
当产品相互引用时,工厂可能陷入循环创建。解决方案:
坑2:多线程安全问题
如果工厂被多线程共享,需要:
坑3:动态库边界
跨DLL使用时,确保:
C++11/14/17提供了新特性,可以写出更现代的抽象工厂实现。
cpp复制using ButtonFactory = std::function<std::unique_ptr<Button>()>;
using CheckboxFactory = std::function<std::unique_ptr<Checkbox>()>;
struct GUIFactories {
ButtonFactory createButton;
CheckboxFactory createCheckbox;
};
auto makeWindowsFactories() {
return GUIFactories{
[] { return std::make_unique<WindowsButton>(); },
[] { return std::make_unique<WindowsCheckbox>(); }
};
}
cpp复制template <typename... Products>
class AbstractFactory {
public:
virtual ~AbstractFactory() = default;
template <typename Product>
std::unique_ptr<Product> create() const {
return static_cast<const ConcreteFactory*>(this)->template create<Product>();
}
};
template <typename... Products>
class WindowsFactory : public AbstractFactory<Products...> {
public:
template <typename Product>
std::unique_ptr<Product> create() const {
if constexpr (std::is_same_v<Product, Button>) {
return std::make_unique<WindowsButton>();
} else if constexpr (std::is_same_v<Product, Checkbox>) {
return std::make_unique<WindowsCheckbox>();
}
// ...
}
};
C++20引入了concept,可以更好地约束工厂接口:
cpp复制template <typename F>
concept GUIFactoryConcept = requires(F f) {
{ f.template create<Button>() } -> std::convertible_to<std::unique_ptr<Button>>;
{ f.template create<Checkbox>() } -> std::convertible_to<std::unique_ptr<Checkbox>>;
};
template <GUIFactoryConcept Factory>
void renderUI(Factory& factory) {
auto btn = factory.template create<Button>();
auto cb = factory.template create<Checkbox>();
// ...
}
以下是整合了现代C++特性的完整实现:
cpp复制// AbstractProducts
class Button {
public:
virtual ~Button() = default;
virtual void render() const = 0;
};
class Checkbox {
public:
virtual ~Checkbox() = default;
virtual void render() const = 0;
};
// ConcreteProducts - Windows
class WindowsButton : public Button {
public:
void render() const override {
std::cout << "Windows风格按钮\n";
}
};
class WindowsCheckbox : public Checkbox {
public:
void render() const override {
std::cout << "Windows风格复选框\n";
}
};
// ConcreteProducts - Linux
class LinuxButton : public Button {
public:
void render() const override {
std::cout << "Linux风格按钮\n";
}
};
class LinuxCheckbox : public Checkbox {
public:
void render() const override {
std::cout << "Linux风格复选框\n";
}
};
// AbstractFactory
class GUIFactory {
public:
virtual ~GUIFactory() = default;
virtual std::unique_ptr<Button> createButton() const = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() const = 0;
};
// ConcreteFactories
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<WindowsCheckbox>();
}
};
class LinuxFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<LinuxButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<LinuxCheckbox>();
}
};
// Client code
void createAndRenderUI(const GUIFactory& factory) {
auto button = factory.createButton();
auto checkbox = factory.createCheckbox();
button->render();
checkbox->render();
}
int main() {
WindowsFactory winFactory;
LinuxFactory linuxFactory;
std::cout << "Windows UI:\n";
createAndRenderUI(winFactory);
std::cout << "\nLinux UI:\n";
createAndRenderUI(linuxFactory);
return 0;
}
要深入掌握抽象工厂模式,我推荐以下资源:
经典书籍:
进阶技巧:
相关模式:
在实际项目中,我经常将抽象工厂与这些模式结合使用,根据具体需求选择最合适的组合方式。记住,设计模式不是银弹,关键是理解其背后的设计原则,然后灵活应用。