# 生成器模式
## 导出数据的应用框架
对于导出数据的应用框架,通常在导出数据上,有一定约定的方式,比如导出成文本格式、数据库备份形式、Excel格式、Xml格式等
导出内容和格式有如下要求:
* 导出的文件,不管什么格式,都分成3个部分,分别是文件头、文件体和文件尾
* 在文件头部分,需要描述以下信息:分公司或者门市点编号、导出数据的日期,对于文本文件,中间用逗号分隔
* 在文本体部分,需要描述以下信息:表名称,然后分条描述数据。对于文本格式,表名称单独一行,数据描述一行算一条数据,字段间用逗号分隔
* 在文件尾部分,需要描述以下信息:输入人
为演示简单,这里只实现生成文本文件格式和Xml格式,其他不考虑。
## 不用模式的解决方案
```cpp
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
/**
* 描述输出文件头的内容的对象
*/
class ExportHeaderModel {
public:
std::string getDepId() const { return depId_; }
void setDepId(const std::string& depId) { depId_ = depId; }
std::string getExportDate() const { return exportDate_; }
void setExportDate(const std::string& exportDate) { exportDate_ = exportDate; }
private:
std::string depId_; // 分公司或门市店的编号
std::string exportDate_; // 导出数据的日期
};
/**
* 描述输出数据的对象
*/
class ExportDataModel {
public:
std::string getProductId() const { return productId_; }
void setProductId(const std::string& productId) { productId_ = productId; }
double getPrice() const { return price_; }
void setPrice(double price) { price_ = price; }
double getAmount() const { return amount_; }
void setAmount(double amount) { amount_ = amount; }
private:
std::string productId_; // 产品编号
double price_; // 销售价格
double amount_; // 销售数量
};
/**
* 描述输出到文件尾的内容的对象
*/
class ExportFooterModel {
public:
std::string getExportUser() const { return exportUser_; }
void setExportUser(const std::string& exportUser) { exportUser_ = exportUser; }
private:
std::string exportUser_; // 输入人
};
/**
* 导出数据到文本文件的对象
*/
class ExportToTxt {
public:
/**
* 描述输出数据的对象
*/
void exportFile(const ExportHeaderModel& ehm,
const std::unordered_map<std::string, std::vector<ExportDataModel> >& mapData,
const ExportFooterModel& efm)
{
std::string buffer;
buffer.append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");
for (auto& it : mapData)
{
buffer.append(it.first + "\n");
for (auto& edm : it.second)
{
buffer.append(edm.getProductId() + "," + std::to_string(edm.getPrice())
+ "," + std::to_string(edm.getAmount()) + "\n");
}
}
buffer.append(efm.getExportUser());
std::cout << buffer << std::endl;
}
};
/**
* 导出数据到文本文件的对象
*/
class ExportToXML {
public:
/**
* 描述输出数据的对象
*/
void exportFile(const ExportHeaderModel& ehm,
const std::unordered_map<std::string, std::vector<ExportDataModel> >& mapData,
const ExportFooterModel& efm)
{
std::string buffer;
buffer.append("<?xml version='1.0' encoding='gb2312'?>\n");
buffer.append("<Report>\n");
buffer.append(" <Header>\n");
buffer.append(" <DepId>" + ehm.getDepId() + "</DepId>\n");
buffer.append(" <ExportDate>" + ehm.getExportDate() + "</ExportDate>\n");
buffer.append(" </Header>\n");
buffer.append(" <Body>\n");
for (auto& it : mapData)
{
buffer.append(" <Datas TableName=\"" + it.first + "\">\n");
for (auto& edm : it.second)
{
buffer.append(" <Data>\n");
buffer.append(" <ProductId>" + edm.getProductId() + "</ProductId>\n");
buffer.append(" <Price>" + std::to_string(edm.getPrice()) + "</Price>\n");
buffer.append(" <Amount>" + std::to_string(edm.getAmount()) + "</Amount>\n");
buffer.append(" </Data>\n");
}
buffer.append(" </Datas>\n");
}
buffer.append(" </Body>\n");
buffer.append(" <Footer>\n");
buffer.append(" <ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
buffer.append(" </Footer>\n");
buffer.append("</Report>\n");
std::cout << buffer << std::endl;
}
};
void test()
{
ExportHeaderModel ehm;
ehm.setDepId("一分公司");
ehm.setExportDate("2020-07-30");
std::unordered_map<std::string, std::vector<ExportDataModel> > mapData;
std::vector<ExportDataModel> col;
ExportDataModel edm1;
edm1.setProductId("产品001号");
edm1.setPrice(100);
edm1.setAmount(80);
ExportDataModel edm2;
edm2.setProductId("产品002号");
edm2.setPrice(99);
edm2.setAmount(55);
col.push_back(edm1);
col.push_back(edm2);
mapData["销售记录表"] = col;
ExportFooterModel efm;
efm.setExportUser("张三");
std::cout << "输出内容到txt文件格式:" << std::endl;
ExportToTxt toTxt;
toTxt.exportFile(ehm, mapData, efm);
std::cout << std::endl << "输出内容到txt文件格式:" << std::endl;
ExportToXML toXml;
toXml.exportFile(ehm, mapData, efm);
}
int main(int argc, char** argv)
{
test();
return 0;
}
```
运行结果:
```
输出内容到txt文件格式:
一分公司,2020-07-30
销售记录表
产品001号,100.000000,80.000000
产品002号,99.000000,55.000000
张三
输出内容到txt文件格式:
<?xml version='1.0' encoding='gb2312'?>
<Report>
<Header>
<DepId>一分公司</DepId>
<ExportDate>2020-07-30</ExportDate>
</Header>
<Body>
<Datas TableName="销售记录表">
<Data>
<ProductId>产品001号</ProductId>
<Price>100.000000</Price>
<Amount>80.000000</Amount>
</Data>
<Data>
<ProductId>产品002号</ProductId>
<Price>99.000000</Price>
<Amount>55.000000</Amount>
</Data>
</Datas>
</Body>
<Footer>
<ExportUser>张三</ExportUser>
</Footer>
</Report>
```
## 不用模式方案问题
观察以上实现,发现不管是输出文本文件还是输出XML文件,实现的基本基本一致,分为以下四步:
1. 先拼接文件头的内容
2. 然后拼接文件体的内容
3. 在拼接文件尾的内容
4. 最后把拼接好的内容输出去称为文件
也就是说,对于不同的输出格式,处理步骤是一样的,但是每步的具体实现是不一样的。按照现在的实现,就存在如下问题:
* 构建每种输出格式的文件时,都会重复这几个步骤,应该提炼出来,形成公共的处理过程
* 今后可能有很多不同的输出格式的要求,这就需要在处理过程不变的情况下,能方便地切换不同的输出格式的处理
也就是说,构建每种格式的数据文件的处理过程,应该和具体的步骤实现分开,这样能够复用处理过程,而且很容易地切换不同的输出格式。
## 生成器模式解决方案
### 生成器模式定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
```cpp
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
/**
* 描述输出文件头的内容的对象
*/
class ExportHeaderModel {
public:
std::string getDepId() const { return depId_; }
void setDepId(const std::string& depId) { depId_ = depId; }
std::string getExportDate() const { return exportDate_; }
void setExportDate(const std::string& exportDate) { exportDate_ = exportDate; }
private:
std::string depId_; // 分公司或门市店的编号
std::string exportDate_; // 导出数据的日期
};
/**
* 描述输出数据的对象
*/
class ExportDataModel {
public:
std::string getProductId() const { return productId_; }
void setProductId(const std::string& productId) { productId_ = productId; }
double getPrice() const { return price_; }
void setPrice(double price) { price_ = price; }
double getAmount() const { return amount_; }
void setAmount(double amount) { amount_ = amount; }
private:
std::string productId_; // 产品编号
double price_; // 销售价格
double amount_; // 销售数量
};
/**
* 描述输出到文件尾的内容的对象
*/
class ExportFooterModel {
public:
std::string getExportUser() const { return exportUser_; }
void setExportUser(const std::string& exportUser) { exportUser_ = exportUser; }
private:
std::string exportUser_; // 输入人
};
/**
* 生成器接口,定义创建一个输出文件对象所需要各个部件的操作
*/
class Builder {
public:
/**
* 构建输出文件的Header部分
*/
virtual void buildHeader(const ExportHeaderModel& ehm) = 0;
/**
* 构建输出文件的Body部分
*/
virtual void buildBody(const std::unordered_map<std::string, std::vector<ExportDataModel> >& mapData) = 0;
/**
* 构建输出文件的Footer部分
*/
virtual void buildFooter(const ExportFooterModel& efm) = 0;
/**
* 返回结果
*/
std::string getResult() const { return buffer; }
protected:
std::string buffer;
};
/**
* 实现导出数据到文本文件的生成器对象
*/
class TxtBuilder : public Builder {
public:
void buildHeader(const ExportHeaderModel& ehm)
{
buffer.append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");
}
void buildBody(const std::unordered_map<std::string, std::vector<ExportDataModel> >& mapData)
{
for (auto& it : mapData)
{
buffer.append(it.first + "\n");
for (auto& edm : it.second)
{
buffer.append(edm.getProductId() + "," + std::to_string(edm.getPrice())
+ "," + std::to_string(edm.getAmount()) + "\n");
}
}
}
void buildFooter(const ExportFooterModel& efm)
{
buffer.append(efm.getExportUser());
}
};
/**
* 实现导出数据到XML文件的生成器对象
*/
class XmlBuilder : public Builder {
public:
void buildHeader(const ExportHeaderModel& ehm)
{
buffer.append("<?xml version='1.0' encoding='gb2312'?>\n");
buffer.append("<Report>\n");
buffer.append(" <Header>\n");
buffer.append(" <DepId>" + ehm.getDepId() + "</DepId>\n");
buffer.append(" <ExportDate>" + ehm.getExportDate() + "</ExportDate>\n");
buffer.append(" </Header>\n");
}
void buildBody(const std::unordered_map<std::string, std::vector<ExportDataModel> >& mapData)
{
buffer.append(" <Body>\n");
for (auto& it : mapData)
{
buffer.append(" <Datas TableName=\"" + it.first + "\">\n");
for (auto& edm : it.second)
{
buffer.append(" <Data>\n");
buffer.append(" <ProductId>" + edm.getProductId() + "</ProductId>\n");
buffer.append(" <Price>" + std::to_string(edm.getPrice()) + "</Price>\n");
buffer.append(" <Amount>" + std::to_string(edm.getAmount()) + "</Amount>\n");
buffer.append(" </Data>\n");
}
buffer.append(" </Datas>\n");
}
buffer.append(" </Body>\n");
}
void buildFooter(const ExportFooterModel& efm)
{
buffer.append(" <Footer>\n");
buffer.append(" <ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
buffer.append(" </Footer>\n");
buffer.append("</Report>\n");
}
};
/**
* 指导者,指导使用生成器的接口来构建输出的文件的对象
*/
class Director {
public:
/**
* 构造方法,传入生成器对象
*/
Director(std::shared_ptr<Builder> builder) : builder_(builder) {}
/**
* 指导生成器构建最终的输出的文件的方法
* @param ehm 文件头的内容
* @param mapData 数据的内容
* @param efm 文件尾的内容
*/
void construct(const ExportHeaderModel& ehm,
const std::unordered_map<std::string, std::vector<ExportDataModel> >& mapData,
const ExportFooterModel& efm)
{
builder_->buildHeader(ehm);
builder_->buildBody(mapData);
builder_->buildFooter(efm);
}
private:
std::shared_ptr<Builder> builder_; //持有当前需要使用的生成器对象
};
void test()
{
ExportHeaderModel ehm;
ehm.setDepId("一分公司");
ehm.setExportDate("2020-07-30");
std::unordered_map<std::string, std::vector<ExportDataModel> > mapData;
std::vector<ExportDataModel> col;
ExportDataModel edm1;
edm1.setProductId("产品001号");
edm1.setPrice(100);
edm1.setAmount(80);
ExportDataModel edm2;
edm2.setProductId("产品002号");
edm2.setPrice(99);
edm2.setAmount(55);
col.push_back(edm1);
col.push_back(edm2);
mapData["销售记录表"] = col;
ExportFooterModel efm;
efm.setExportUser("张三");
// txt格式
std::shared_ptr<Builder> txtBuilder_ptr(new TxtBuilder());
Director txt_director(txtBuilder_ptr);
txt_director.construct(ehm, mapData, efm);
std::cout << "输出内容到txt文件格式:" << std::endl;
std::cout << txtBuilder_ptr->getResult() << std::endl << std::endl;
// XML格式
std::shared_ptr<Builder> xmlBuilder_ptr(new XmlBuilder());
Director xml_director(xmlBuilder_ptr);
xml_director.construct(ehm, mapData, efm);
std::cout << "输出内容到XML文件格式:" << std::endl;
std::cout << xmlBuilder_ptr->getResult() << std::endl;
}
int main(int argc, char** argv)
{
test();
return 0;
}
```
运行结果和上面一样
上面的示例其实就是生成器模式,其实挺简单。对同一个构建过程,只要配置不同的生成器实现,就可以生成不同表现的对象,就是生成器模式的实现方式和优势所在。
## 生成器模式的本质
**分离整体构建算法和部件构造**
生成器模式的构建过程是统一的、固定不变的,变化的部分放到程程器部分,只要配置不同的生成器,那么同样的构建过程,就能构建出不同的产品来。
- 空白目录
- 算法
- 排序
- 冒泡排序
- 选择排序
- 插入排序
- 归并排序
- 快速排序
- 计数排序
- 桶排序
- 基数排序
- 希尔排序
- 堆排序
- 二分查找
- 最小堆
- 最小索引堆
- 平衡二叉树(AVL tree)
- bitmap位图
- 布隆过滤器
- hashmap
- topK
- 跳表
- LRU Cache
- kmp
- 最小堆和堆排序
- 最短路径
- C++
- 运行时类型判断RTTI
- C++反射
- 手动实现智能指针
- 序列化实现
- rpc实现
- std::forward
- 函数指针的妙用
- C/C++
- std::function
- 同步队列
- 线程池实现
- std::promise
- 深入理解虚函数
- extern "C" 关键字讲解
- 大端小端的区别
- 简历
- 简历1
- redis
- 数据结构和对象
- sds
- list
- zskiplist
- 腾讯云redis面试题总结
- redis集群部署
- LeetCode
- 目标
- go基础
- 算法快速入门
- 数据结构篇
- 二叉树
- 链表
- 栈和队列
- 二进制
- 基础算法篇
- 二分搜索
- 排序算法
- 动态规划
- 算法思维
- 递归思维
- 滑动窗口思想
- 二叉搜索树
- 回溯法
- 其他
- 剑指offer
- 笔记
- git代理加速
- Linux
- vim大法
- vscode远程不能跳转
- cmake
- 设计模式
- 单例模式
- 简单工厂模式
- 外观模式
- 适配器模式
- 工厂方法模式
- 抽象工厂模式
- 生成器模式
- 原型模式
- 中介者模式
- 观察者模式
- 访问者模式
- 命令模式
- 网络编程
- epoll reactor模式
- linux timerfd系列函数总结
- IO
- mapreduce
- 反射器
- leo通信库
- Mutex
- Condition
- thread
- raft
- 协程
- hook
- 定时器
- 别人的面试经验
- 面试题
- vector崩溃问题
- JAVA
- Linux java环境配置
- ucore
- lab1
- FreeNOS
- leveldb
- 刷题笔记
- 回文串
- 前缀树
- 字符串查找
- 查找两个字符串a,b中的最长公共子串
- 动态规划
- golang
- 顺序循环打印实现
- 数据结构
- rpc运用
- python
- 单例
- 深拷贝浅拷贝
- 链表
- python基础题
- mysql
- 事务
- Linux
- 共享内存
- 刷题记录
- 贪心算法
- 动态规划
- 面试
- 腾讯C++面试
- 微众面试JD
- 迅雷网络面试
- 学习网址
- rabbitMq
