建造者模式解决C++中因可选参数多导致构造函数冗长难维护的问题,通过分离构建过程与表示,提升代码可读性和扩展性。

在C++中,使用建造者模式来构建复杂对象,本质上是为了解决那些拥有众多可选参数、构造函数签名冗长且难以维护的问题。它将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建出不同的表示。这意味着,当你的对象有太多配置选项,或者构建步骤本身就很复杂时,建造者模式提供了一种更清晰、更可控的方式来组装它们,避免了“伸缩构造器”的噩梦。
建造者模式的核心思想是:不直接实例化一个复杂对象,而是通过一个专门的“建造者”对象,一步步地设置对象的各个部分,最后由建造者返回最终构建好的对象。这让对象的创建过程变得更加模块化和可读。
我们以构建一台电脑为例。一台电脑可以有不同的CPU、内存、硬盘、显卡、操作系统等等。如果用传统构造函数,你可能需要一个接受十几个参数的构造函数,或者写一堆重载的构造函数,这很快就会失控。
核心组件:
立即学习“C++免费学习笔记(深入)”;
Computer
buildCPU()
buildRAM()
buildStorage()
C++代码示例:
#include <iostream>
#include <string>
#include <vector>
#include <memory> // For std::unique_ptr
// 1. 产品 (Product)
class Computer {
public:
void setCPU(const std::string& cpu) { this->cpu_ = cpu; }
void setRAM(const std::string& ram) { this->ram_ = ram; }
void setStorage(const std::string& storage) { this->storage_ = storage; }
void setGPU(const std::string& gpu) { this->gpu_ = gpu; }
void setOS(const std::string& os) { this->os_ = os; }
void showConfiguration() const {
std::cout << "--- Computer Configuration ---" << std::endl;
std::cout << "CPU: " << cpu_ << std::endl;
std::cout << "RAM: " << ram_ << std::endl;
std::cout << "Storage: " << storage_ << std::endl;
if (!gpu_.empty()) {
std::cout << "GPU: " << gpu_ << std::endl;
}
if (!os_.empty()) {
std::cout << "OS: " << os_ << std::endl;
}
std::cout << "-----------------------------" << std::endl;
}
private:
std::string cpu_;
std::string ram_;
std::string storage_;
std::string gpu_;
std::string os_;
};
// 2. 抽象建造者 (Builder)
class ComputerBuilder {
public:
virtual ~ComputerBuilder() = default;
virtual ComputerBuilder* buildCPU(const std::string& cpu) = 0;
virtual ComputerBuilder* buildRAM(const std::string& ram) = 0;
virtual ComputerBuilder* buildStorage(const std::string& storage) = 0;
virtual ComputerBuilder* buildGPU(const std::string& gpu) = 0;
virtual ComputerBuilder* buildOS(const std::string& os) = 0;
virtual std::unique_ptr<Computer> getResult() = 0;
};
// 3. 具体建造者 (Concrete Builder)
class GamingComputerBuilder : public ComputerBuilder {
public:
GamingComputerBuilder() {
computer_ = std::make_unique<Computer>();
}
ComputerBuilder* buildCPU(const std::string& cpu) override {
computer_->setCPU(cpu);
return this;
}
ComputerBuilder* buildRAM(const std::string& ram) override {
computer_->setRAM(ram);
return this;
}
ComputerBuilder* buildStorage(const std::string& storage) override {
computer_->setStorage(storage);
return this;
}
ComputerBuilder* buildGPU(const std::string& gpu) override {
computer_->setGPU(gpu);
return this;
}
ComputerBuilder* buildOS(const std::string& os) override {
computer_->setOS(os);
return this;
}
std::unique_ptr<Computer> getResult() override {
// 在这里可以添加构建前的验证逻辑
if (computer_->getCPU().empty() || computer_->getRAM().empty()) {
std::cerr << "Error: CPU and RAM are mandatory for a gaming computer!" << std::endl;
return nullptr; // 或者抛出异常
}
return std::move(computer_);
}
private:
std::unique_ptr<Computer> computer_;
// 为了示例,Computer类中添加了get方法
// 实际项目中,通常会通过friend class或者在builder内部直接访问私有成员
// 为了编译通过,这里假设Computer有这些get方法
// 更好的做法是让Builder成为Computer的友元,或者Computer提供内部设置接口
};
// 为了示例编译通过,在Computer类中添加get方法
// 实际生产代码中,可能通过友元类或者其他方式访问
namespace { // 匿名命名空间,避免链接冲突
std::string getCPU(const Computer& c) { return c.cpu_; }
std::string getRAM(const Computer& c) { return c.ram_; }
}
// 4. 指挥者 (Director, 可选)
class ComputerAssembler {
public:
void setBuilder(ComputerBuilder* builder) {
builder_ = builder;
}
std::unique_ptr<Computer> assembleBasicGamingPC() {
if (!builder_) {
std::cerr << "Builder not set!" << std::endl;
return nullptr;
}
return builder_->buildCPU("Intel i7-13700K")
->buildRAM("32GB DDR5")
->buildStorage("1TB NVMe SSD")
->buildGPU("NVIDIA RTX 4070")
->buildOS("Windows 11 Pro")
->getResult();
}
std::unique_ptr<Computer> assembleHighEndGamingPC() {
if (!builder_) {
std::cerr << "Builder not set!" << std::endl;
return nullptr;
}
return builder_->buildCPU("Intel i9-14900K")
->buildRAM("64GB DDR5")
->buildStorage("2TB NVMe SSD")
->buildGPU("NVIDIA RTX 4090")
->buildOS("Windows 11 Pro")
->getResult();
}
private:
ComputerBuilder* builder_ = nullptr;
};
// 客户端代码
int main() {
// 不使用Director,直接用Builder
std::cout << "Building a custom gaming PC directly:" << std::endl;
GamingComputerBuilder customBuilder;
std::unique_ptr<Computer> myCustomPC = customBuilder.buildCPU("AMD Ryzen 7 7800X3D")
->buildRAM("32GB DDR5")
->buildStorage("2TB NVMe SSD")
->buildGPU("AMD Radeon RX 7900 XTX")
->buildOS("Ubuntu Linux")
->getResult();
if (myCustomPC) {
myCustomPC->showConfiguration();
}
std::cout << std::endl;
// 使用Director
std::cout << "Building PCs using Director:" << std::endl;
GamingComputerBuilder gamingBuilder;
ComputerAssembler assembler;
assembler.setBuilder(&gamingBuilder);
std::unique_ptr<Computer> basicGamingPC = assembler.assembleBasicGamingPC();
if (basicGamingPC) {
basicGamingPC->showConfiguration();
}
std::cout << std::endl;
std::unique_ptr<Computer> highEndGamingPC = assembler.assembleHighEndGamingPC();
if (highEndGamingPC) {
highEndGamingPC->showConfiguration();
}
std::cout << std::endl;
return 0;
}(注:为了代码示例的简洁性,
Computer
getCPU()
getRAM()
Computer
在C++里,当你面对一个拥有大量配置选项或组件的对象时,我个人觉得,传统构造函数的方式很快就会让你陷入泥潭。最常见的问题就是所谓的“伸缩构造器”(telescoping constructor anti-pattern)。设想一下,一个
Car
Car(Color c, EngineType et)
Car(Color c, EngineType et, SeatMaterial sm)
Car(Color c, EngineType et, SeatMaterial sm, WheelStyle ws)
这简直是灾难。构造函数列表会变得异常庞大,而且随着可选参数的增加,你必须为每个参数组合创建新的构造函数。这不仅让代码量激增,更关键的是,它使得客户端代码变得难以阅读和理解。当你看到
new Car(RED, V8, LEATHER, SPORTY, true, false)
true
false
此外,这种方式还容易导致错误。如果你不小心调换了两个相同类型的参数的顺序,编译器可能不会报错,但你的对象就会被错误地初始化。而且,如果对象在构造后是不可变的(immutable),那么这种一次性传入所有参数的构造方式,在参数过多时,其复杂性是指数级上升的。我曾为此头疼不已,每次要添加一个新功能或新配置,都得去修改一堆构造函数,那感觉就像是在拆一个随时可能爆炸的炸弹。
本文档主要讲述的是j2me3D游戏开发简单教程; 如今,3D图形几乎是任何一部游戏的关键部分,甚至一些应用程序也通过用3D形式来描述信息而获得了成功。如前文中所述,以立即模式和手工编码建立所有的3D对象的方式进行开发速度很慢且很复杂。应用程序中多边形的所有角点必须在数组中独立编码。在JSR 184中,这称为立即模式。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
0
建造者模式在可读性和可扩展性方面带来的提升,在我看来是实实在在的。它改变了我们思考对象创建的方式,从“一次性全部塞进去”变成了“一步步精雕细琢”。
可读性方面:
*this
builder->buildCPU()->buildRAM()->getResult()
可扩展性方面:
GamingComputer
OfficeComputer
Computer
SoundCard
buildSoundCard()
可以说,建造者模式将复杂的对象创建逻辑封装起来,让客户端代码变得更加简洁、富有表达力。我个人非常喜欢这种模式,因为它能把一堆混乱的参数列表,整理成清晰、有条理的构建流程。
在我实际使用建造者模式的过程中,确实遇到过一些坑,也总结了一些经验。要让这个模式发挥最大效用,同时避免引入新的复杂性,你需要注意以下几点:
常见陷阱:
name
id
void
buildXxx
*this
void
private
protected
friend class
std::unique_ptr
std::shared_ptr
getResult()
getResult()
最佳实践:
std::unique_ptr<Product>
getResult()
std::move
getResult()
getResult()
private
protected
建造者模式是一个强大的工具,但像所有工具一样,它需要被正确地使用。避免盲目应用,理解其背后的设计哲学,才能真正让它为你的C++项目带来清晰和秩序。
以上就是C++如何使用建造者模式构建复杂对象的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号