在C++项目中,对象创建往往是系统复杂度的主要来源之一。我们经常遇到这样的场景:需要根据运行时条件动态创建对象、希望控制实例数量以避免资源浪费、或者想要简化复杂对象的构造过程。这些问题如果直接用new/delete和常规构造函数来处理,代码很快就会变得难以维护。
创建型设计模式正是为解决这类问题而生。它们封装了对象创建的具体过程,让代码更灵活、更可维护。比如:
我在参与一个跨平台渲染引擎开发时,就曾因为滥用new运算符导致内存泄漏频发。后来通过系统应用工厂方法模式,将对象创建集中管理,不仅解决了内存问题,还使新增渲染后端类型的成本降低了70%。
C++11后的单例模式实现已经变得简单可靠:
cpp复制class ConfigManager {
public:
static ConfigManager& getInstance() {
static ConfigManager instance; // C++11保证线程安全
return instance;
}
// 删除拷贝构造和赋值运算符
ConfigManager(const ConfigManager&) = delete;
void operator=(const ConfigManager&) = delete;
private:
ConfigManager() = default; // 私有构造函数
~ConfigManager() = default;
};
关键点:利用函数局部静态变量的特性,既实现了延迟初始化,又天然线程安全。这是现代C++最推荐的单例实现方式。
在游戏开发中,我们曾用单例管理纹理资源,但遇到了一个典型问题:不同渲染线程同时访问导致竞争条件。最终解决方案是:
cpp复制// 线程安全的资源访问示例
Texture* TextureManager::getTexture(const std::string& name) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = textures_.find(name);
if (it != textures_.end()) {
return it->second.get();
}
auto texture = std::make_unique<Texture>(name);
auto* ptr = texture.get();
textures_[name] = std::move(texture);
return ptr;
}
假设我们正在开发一个文档编辑器,需要支持多种文档格式:
cpp复制class Document {
public:
virtual void save() = 0;
virtual ~Document() = default;
};
class DocumentFactory {
public:
virtual std::unique_ptr<Document> createDocument() = 0;
virtual ~DocumentFactory() = default;
};
// 具体实现
class PDFDocument : public Document { /*...*/ };
class WordDocument : public Document { /*...*/ };
class PDFFactory : public DocumentFactory {
public:
std::unique_ptr<Document> createDocument() override {
return std::make_unique<PDFDocument>();
}
};
在开发跨平台UI框架时,我们发现经典工厂方法存在扩展性问题。最终采用的改进方案:
cpp复制template <typename T>
class GenericFactory : public DocumentFactory {
public:
std::unique_ptr<Document> createDocument() override {
return std::make_unique<T>();
}
};
// 注册工厂
auto pdfFactory = std::make_unique<GenericFactory<PDFDocument>>();
cpp复制// 工厂注册表
std::map<std::string, std::function<std::unique_ptr<Document>()>> factories;
// 注册工厂
factories["pdf"] = [] { return std::make_unique<PDFDocument>(); };
// 动态创建
auto doc = factories[format]();
这种改进使新增文档类型的代码量减少了60%,特别适合插件式架构。
code复制AbstractFactory
├── createButton()
└── createCheckbox()
WinFactory : AbstractFactory
├── createButton() → WinButton
└── createCheckbox() → WinCheckbox
MacFactory : AbstractFactory
├── createButton() → MacButton
└── createCheckbox() → MacCheckbox
在开发跨平台数据库工具时,我们这样抽象不同数据库的UI组件:
cpp复制class DatabaseButton {
public:
virtual void render() = 0;
virtual void onClick() = 0;
};
class MySQLButton : public DatabaseButton { /*...*/ };
class OracleButton : public DatabaseButton { /*...*/ };
class GUIFactory {
public:
virtual std::unique_ptr<DatabaseButton> createButton() = 0;
// 其他UI组件...
};
// 具体工厂
class MySQLFactory : public GUIFactory {
public:
std::unique_ptr<DatabaseButton> createButton() override {
auto button = std::make_unique<MySQLButton>();
button->setConnectionParams(/*...*/);
return button;
}
};
我们发现抽象工厂可能导致对象创建开销增大,通过以下方式优化:
cpp复制class ButtonPrototype {
public:
virtual std::unique_ptr<ButtonPrototype> clone() = 0;
};
class MySQLButtonPrototype : public ButtonPrototype {
public:
std::unique_ptr<ButtonPrototype> clone() override {
auto button = std::make_unique<MySQLButton>();
button->setConnectionParams(this->params_); // 共享基础配置
return button;
}
private:
ConnectionParams params_;
};
现代C++项目更倾向于使用流式建造者:
cpp复制class QueryBuilder {
public:
QueryBuilder& select(const std::vector<std::string>& columns) {
query_.selectColumns = columns;
return *this;
}
QueryBuilder& where(const std::string& condition) {
query_.conditions.push_back(condition);
return *this;
}
Query build() && { // 右值引用限定
if (query_.selectColumns.empty()) {
throw std::logic_error("No columns selected");
}
return std::move(query_);
}
private:
Query query_;
};
// 使用示例
auto query = QueryBuilder()
.select({"id", "name"})
.where("age > 18")
.where("status = 1")
.build();
通过分阶段建造者确保构建顺序正确:
cpp复制class SQLQuery {
// 仅声明
class Builder;
class FromBuilder;
class WhereBuilder;
public:
static Builder create();
};
// 分阶段实现
class SQLQuery::Builder {
public:
FromBuilder select(std::vector<std::string> cols) {
query_.selectCols = std::move(cols);
return FromBuilder{query_};
}
};
class SQLQuery::FromBuilder {
public:
WhereBuilder from(std::string table) {
query_.table = std::move(table);
return WhereBuilder{query_};
}
};
// 使用示例
auto query = SQLQuery::create()
.select({"id", "name"})
.from("users")
.where("age > 18")
.build();
这种设计在编译器层面就确保了必须按select→from→where的顺序调用,比运行时检查更可靠。
游戏中的NPC角色通常适合用原型模式:
cpp复制class NPC {
public:
virtual std::unique_ptr<NPC> clone() = 0;
virtual void spawn() = 0;
};
class Orc : public NPC {
public:
std::unique_ptr<NPC> clone() override {
auto copy = std::make_unique<Orc>();
copy->health_ = this->health_;
copy->position_ = this->position_;
return copy;
}
void spawn() override {
// 生成逻辑...
}
private:
int health_;
Vector3 position_;
};
实际项目中我们会使用原型管理器:
cpp复制class NPCPrototypeRegistry {
public:
void registerPrototype(const std::string& id, std::unique_ptr<NPC> proto) {
prototypes_[id] = std::move(proto);
}
std::unique_ptr<NPC> spawn(const std::string& id) {
return prototypes_.at(id)->clone();
}
private:
std::unordered_map<std::string, std::unique_ptr<NPC>> prototypes_;
};
// 初始化
registry.registerPrototype("orc_warrior", std::make_unique<Orc>(/*...*/));
registry.registerPrototype("orc_shaman", std::make_unique<Orc>(/*...*/));
// 生成NPC
auto npc = registry.spawn("orc_warrior");
cpp复制class SmartNPC : public NPC {
public:
std::unique_ptr<NPC> clone() override {
auto copy = std::make_unique<SmartNPC>();
copy->sharedData_ = this->sharedData_; // 共享引用计数数据
copy->uniqueData_ = std::make_unique<UniqueData>(*this->uniqueData_);
return copy;
}
private:
std::shared_ptr<SharedData> sharedData_;
std::unique_ptr<UniqueData> uniqueData_;
};
在开发分布式计算框架时,我们综合运用了多种创建型模式:
cpp复制// 伪代码示例
auto task = TaskBuilder()
.withAlgorithm(AlgorithmFactory::create("kmeans"))
.withData(prototypeDataset.clone())
.withParameters(/*...*/)
.build();
MasterNode::getInstance().submitTask(std::move(task));
这种设计使得框架可以:
在性能测试中,这种设计比传统面向对象实现减少了35%的内存分配次数,任务提交吞吐量提升了2倍。