工厂与抽象工厂
本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 92 课,共 104 课。
工厂模式将对象的创建交给单独的方法或类,这样你无需指定对象的确切类即可创建对象。当创建逻辑复杂,或你想让客户端代码与具体实现解耦时,这种模式就很有用。
简单工厂使用一个方法,根据输入返回不同的派生类型:
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// 工厂函数
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}抽象工厂通过创建相关对象的系列,进一步扩展了这种做法。你不再只有一个工厂方法,而是拥有一个包含多个创建方法的工厂接口:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// 抽象工厂
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};需要灵活地创建对象时,使用 Factory;需要确保相关对象始终协同工作时,使用 Abstract Factory。
挑战
简单让我们使用工厂模式和抽象工厂模式构建一个文档生成器系统。你将创建一个系统,为不同的输出格式(PDF 和 HTML)生成不同类型的文档(报告和发票),并展示工厂如何将对象创建与客户端代码解耦。
你将把代码组织在 4 个文件中:
Document.h:定义文档产品层次结构。创建一个抽象基类
Document,其中包含一个纯虚方法generate(),用于返回描述所生成文档的字符串,并包含一个虚析构函数。然后创建 4 个具体的文档类:
PDFReport:generate()返回"Generating PDF Report"HTMLReport:generate()返回"Generating HTML Report"PDFInvoice:generate()返回"Generating PDF Invoice"HTMLInvoice:generate()返回"Generating HTML Invoice"
DocumentFactory.h:实现一个简单工厂函数和一个抽象工厂。首先,创建一个名为
createDocument的简单工厂函数,它接收两个字符串:文档类型("report"或"invoice")和格式("pdf"或"html")。它应返回一个指向相应具体文档的std::unique_ptr<Document>。如果组合无效,则返回nullptr。然后,创建一个名为
DocumentFactory的抽象工厂类,其中包含两个纯虚方法:createReport():返回一个std::unique_ptr<Document>createInvoice():返回一个std::unique_ptr<Document>
实现两个具体工厂:
PDFFactory:创建PDFReport和PDFInvoiceHTMLFactory:创建HTMLReport和HTMLInvoice
DocumentService.h:创建一个使用抽象工厂的服务。定义一个
DocumentService类,在其构造函数中接收一个std::unique_ptr<DocumentFactory>。它应包含一个名为generateAllDocuments()的方法,该方法使用工厂创建一个报告和一张发票,然后分别调用它们的generate()并打印结果,每行一个。main.cpp:演示这两种工厂方式。读取两个输入:
- 文档类型(
report或invoice) - 格式(
pdf或html)
首先,根据输入使用简单工厂函数创建一个文档。打印调用其
generate()的结果。然后打印一个空行。
接下来,根据格式输入创建一个使用相应工厂的
DocumentService(输入为pdf时使用PDFFactory,输入为html时使用HTMLFactory)。调用generateAllDocuments(),展示抽象工厂如何创建一组相关文档。- 文档类型(
例如,输入 report 和 pdf 时:
Generating PDF Report
Generating PDF Report
Generating PDF Invoice输入 invoice 和 html 时:
Generating HTML Invoice
Generating HTML Report
Generating HTML Invoice注意,简单工厂会根据两个参数创建单个文档,而抽象工厂会确保服务创建的所有文档都采用相同的格式。这就是抽象工厂的主要优势:当你需要一组能够协调一致地协同工作的相关对象时,它能满足你的需求。
亲手试试
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// 读取输入
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: 使用简单工厂函数创建文档
// 根据 documentType 和 format 输入
// 输出对该文档调用 generate() 的结果
// 输出一个空行
cout << endl;
// TODO: 使用合适的工厂创建 DocumentService
// 根据 format 输入选择("pdf" 对应 PDFFactory,"html" 对应 HTMLFactory)
// 调用 generateAllDocuments(),演示抽象工厂
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器