目录
场景
针对学习工厂方法模式时,提到了导出数据的框架。
我们当时使用创建器(抽象类)里面提供的抽象方法(返回值为接口也就是Product),通过继承这个抽象类区分不同的导出数据方法。最后在客户端调用具体的创建器实现来完成。
现在我们需要在导出的内容和格式上做出要求。
代码示例
描述文件头的对象
package day07生成器模式;
/**
* 文件头
*/
public class ExportHeaderModel {
/**
* 编号
*/
private String depId;
/**
* 导出日期
*/
private String exportDate;
public String getDepId() {
return depId;
}
public void setDepId(String depId) {
this.depId = depId;
}
public String getExportDate() {
return exportDate;
}
public void setExportDate(String exportDate) {
this.exportDate = exportDate;
}
}
描述文件内容的
package day07生成器模式;
/**
* 描述输出数据的对象
*/
public class ExportDataModel {
/**
* 产品编号
*/
private String productId;
/**
* 销售价格
*/
private double price;
/**
* 销售数量
*/
private double amount;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
描述文件尾的
package day07生成器模式;
/**
* 输出到文件尾的内容
*/
public class ExportFooterModel {
/**
* 输出人
*/
private String exportUser;
public String getExportUser() {
return exportUser;
}
public void setExportUser(String exportUser) {
this.exportUser = exportUser;
}
}
具体的导出实现,导出到txt
package day07生成器模式;
import java.util.Collection;
import java.util.Map;
/**
* 具体的导出实现
* 导出数据到文本文件的对象
*/
public class ExportToTxt {
public void export(ExportHeaderModel ehm, Map<String, Collection<ExportDataModel>> mapData,ExportFooterModel efm){
StringBuffer buffer = new StringBuffer();
// 1. 拼接文件头
buffer.append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");
// 2. 拼接文件体内容
for (String s : mapData.keySet()) {
// 先拼接表名称
buffer.append(s + "\n");
// 再循环拼接具体数据
for (ExportDataModel edm : mapData.get(s)) {
buffer.append(edm.getProductId() + "," + edm.getPrice() + "," + edm.getAmount() + "\n");
}
}
// 3. 拼接文件尾
buffer.append(efm.getExportUser());
System.out.println("输出到文本文件的内容: \n" + buffer);
}
}
导出到xml
package day07生成器模式;
import java.util.Collection;
import java.util.Map;
/**
* 具体的导出实现
* 导出数据到xml文件的对象
*/
public class ExportToXml {
public void export(ExportHeaderModel ehm, Map<String, Collection<ExportDataModel>> mapData,ExportFooterModel efm){
StringBuffer buffer = new StringBuffer();
// 1. 拼接文件头
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");
// 2. 拼接文件体内容
buffer.append("<Body>\n");
for (String s : mapData.keySet()) {
// 先拼接表名称
buffer.append(" <Datas TableName=\"" + s + "\">\n");
// 然后驯悍拼接具体数据
for (ExportDataModel edm : mapData.get(s)) {
buffer.append(" <Data>\n");
buffer.append(" <ProductId>\n" + edm.getProductId() + "</ProductId>\n");
buffer.append(" <Price>\n" + edm.getPrice() + "</Price>\n");
buffer.append(" <Amount>\n" + edm.getAmount() + "</Amount>\n");
buffer.append(" </Data>\n");
}
buffer.append(" </Datas>\n");
}
buffer.append("</Body>\n");
// 3. 拼接文件尾
buffer.append(" <Footer>\n");
buffer.append(" <ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
buffer.append(" </Footer>\n");
buffer.append(" </Report>\n");
System.out.println("输出到文本文件的内容: \n" + buffer);
}
}
客户端
package day07生成器模式;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
public class Client {
public static void main(String[] args) {
// 准备测试数据
ExportHeaderModel ehm = new ExportHeaderModel();
ehm.setExportDate("2023-08-19");
ehm.setDepId("一分公司");
HashMap<String, Collection<ExportDataModel>> mapData = new HashMap<>();
ArrayList<ExportDataModel> col = new ArrayList<>();
ExportDataModel edm1 = new ExportDataModel();
edm1.setProductId("产品001号");
edm1.setPrice(100);
edm1.setAmount(80);
ExportDataModel edm2 = new ExportDataModel();
edm2.setProductId("产品002号");
edm2.setPrice(99);
edm2.setAmount(55);
col.add(edm1);
col.add(edm2);
mapData.put("销售记录表", col);
ExportFooterModel efm = new ExportFooterModel();
efm.setExportUser("张三");
ExportToTxt toTxt = new ExportToTxt();
toTxt.export(ehm , mapData , efm);
ExportToXml exportToXml = new ExportToXml();
exportToXml.export(ehm , mapData , efm);
}
}
有何问题
发现不管是输出到txt还是xml都有共同的步骤,拼接头,拼接数据,拼接尾,最后输出成为文件。
处理步骤是一样的,具体实现不一样,因此将处理步骤应该提炼出来,形成公共的处理过程。易复用。
解决方案
生成器模式
定义
生成器结构图
解决思路
将构建过独立出来,当作指导者,由它来指导装配过程,但是不负责每一步具体的实现。
光有指导者是不够的,必须要有能具体实现每步的对象,这些实现对象称作生成器。
使用实例:
三个数据模型对象不动(头,数据,尾应该展示的东西)
Build接口:生成器接口,定义创建一个输出文件对象所需的各个部件的操作
package day07生成器模式.实例;
import day07生成器模式.ExportDataModel;
import day07生成器模式.ExportFooterModel;
import day07生成器模式.ExportHeaderModel;
import java.util.Collection;
import java.util.Map;
/**
* 生成器接口,定义创建一个输出文件对象所需的各个部件的操作
*/
public interface Builder {
/**
* 构建输出文件的Header部分
* @param ehm
*/
public void buildHeader(ExportHeaderModel ehm);
/**
* 构建输出文件的Data部分
* @param mapData
*/
public void buildBody(Map<String, Collection<ExportDataModel>> mapData);
/**
* 构建输出文件的Footer部分
* @param efm
*/
public void buildFooter(ExportFooterModel efm);
}
具体的生成器实现TxtBuild,对Build进行实现
package day07生成器模式.实例;
import day07生成器模式.ExportDataModel;
import day07生成器模式.ExportFooterModel;
import day07生成器模式.ExportHeaderModel;
import java.util.Collection;
import java.util.Map;
/**
* 具体的生成器实现
* 导出数据到文本文件
*/
public class TxtBuilder implements Builder{
/**
* 用来记录构建的文件的内容,相当于产品
*/
private StringBuffer buffer = new StringBuffer();
@Override
public void buildHeader(ExportHeaderModel ehm) {
buffer.append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");
}
@Override
public void buildBody(Map<String, Collection<ExportDataModel>> mapData) {
for (String s : mapData.keySet()) {
// 先拼接表名
buffer.append(s + "\n");
// 循环拼接具体数据
for (ExportDataModel edm : mapData.get(s)) {
buffer.append(edm.getProductId() + "," + edm.getPrice() + "," + edm.getAmount() + "\n");
}
}
}
@Override
public void buildFooter(ExportFooterModel efm) {
buffer.append(efm.getExportUser());
}
public StringBuffer getResult(){
return buffer;
}
}
XmlBuild实现
package day07生成器模式.实例;
import day07生成器模式.ExportDataModel;
import day07生成器模式.ExportFooterModel;
import day07生成器模式.ExportHeaderModel;
import java.util.Collection;
import java.util.Map;
public class XmlBuilder implements Builder{
/**
* 用来记录构建的文件的内容,相当于产品
*/
private StringBuffer buffer = new StringBuffer();
@Override
public void buildHeader(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");
}
@Override
public void buildBody(Map<String, Collection<ExportDataModel>> mapData) {
buffer.append("<Body>\n");
for (String s : mapData.keySet()) {
// 先拼接表名称
buffer.append(" <Datas TableName=\"" + s + "\">\n");
// 然后驯悍拼接具体数据
for (ExportDataModel edm : mapData.get(s)) {
buffer.append(" <Data>\n");
buffer.append(" <ProductId>\n" + edm.getProductId() + "</ProductId>\n");
buffer.append(" <Price>\n" + edm.getPrice() + "</Price>\n");
buffer.append(" <Amount>\n" + edm.getAmount() + "</Amount>\n");
buffer.append(" </Data>\n");
}
buffer.append(" </Datas>\n");
}
buffer.append("</Body>\n");
}
@Override
public void buildFooter(ExportFooterModel efm) {
buffer.append(" <Footer>\n");
buffer.append(" <ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
buffer.append(" </Footer>\n");
buffer.append(" </Report>\n");
}
public StringBuffer getResult(){
return buffer;
}
}
解释:这两个实现都是对Build中头、数据、尾方法的实现,只不过之前是写一起了,现在进行拆分,并提供一个返回整体对象的方法getResult()
生成器有了,现在创建一个指导者
package day07生成器模式.实例;
import day07生成器模式.ExportDataModel;
import day07生成器模式.ExportFooterModel;
import day07生成器模式.ExportHeaderModel;
import java.util.Collection;
import java.util.Map;
/**
* 指导者,指导生成器实现进行具体的产品构建
*/
public class Director {
/**
* 持有当前需要使用的生成器对象
*/
private Builder builder;
/**
* 构造方法
* @param builder 生成器对象
*/
public Director(Builder builder){
this.builder = builder;
}
public void construct(ExportHeaderModel ehm, Map<String, Collection<ExportDataModel>> mapData, ExportFooterModel efm){
//1. 先构建Header
builder.buildHeader(ehm);
//2. 然后构建Body
builder.buildBody(mapData);
//3. 再构建Footer
builder.buildFooter(efm);
}
}
解释:
1.指导者顾名思义要指导什么东西,那么得有这个东西吧,所以持有这个Build接口,并且在创建的时候接入这个build。
2.怎么指导呢?指导方法construct
Client客户端测试
package day07生成器模式.实例;
import day07生成器模式.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
public class Client {
public static void main(String[] args) {
// 准备测试数据
ExportHeaderModel ehm = new ExportHeaderModel();
ehm.setExportDate("2023-08-19");
ehm.setDepId("一分公司");
HashMap<String, Collection<ExportDataModel>> mapData = new HashMap<>();
ArrayList<ExportDataModel> col = new ArrayList<>();
ExportDataModel edm1 = new ExportDataModel();
edm1.setProductId("产品001号");
edm1.setPrice(100);
edm1.setAmount(80);
ExportDataModel edm2 = new ExportDataModel();
edm2.setProductId("产品002号");
edm2.setPrice(99);
edm2.setAmount(55);
col.add(edm1);
col.add(edm2);
mapData.put("销售记录表", col);
ExportFooterModel efm = new ExportFooterModel();
efm.setExportUser("张三");
// ExportToTxt toTxt = new ExportToTxt();
// toTxt.export(ehm , mapData , efm);
//
// ExportToXml exportToXml = new ExportToXml();
// exportToXml.export(ehm , mapData , efm);
// 测试输出到文本文件
TxtBuilder txtBuilder = new TxtBuilder();
// 创建指导者
Director director = new Director(txtBuilder);
director.construct(ehm,mapData,efm);
System.out.println("输出到文本文件: \n"+txtBuilder.getResult());
System.out.println("===========================");
// 测试输出到Xml文件
XmlBuilder xmlBuilder = new XmlBuilder();
// 创建指导者
Director director1 = new Director(xmlBuilder);
director1.construct(ehm,mapData,efm);
System.out.println("输出到xml文件 \n"+xmlBuilder.getResult());
}
}
解释:更改了之前具体得创建方法,现在先用build实现类定义我要输出成什么格式,实现类写完后得有人指导实现类进行构建啊,那我就创建一个Director,入参传入实现类并调用构建方法构建。
具体的构建还是通过build的实现来构建的,它怎么知道是谁呢?因为我们在构造方法中传入了build,那么传入的是Txt那就是Txt构建,传入的是Xml,那就用xml构建
模式讲解
生成器模式的重心在于分离构建算法和具体的构造方法。我的指导者只是把Buildj接口拿过来,内部提供构建的方法也是针对接口中方法的顺序进行一步一步搭建,但是这些方法的具体实现是在他们各自的Build实现类里面(TxtBuilder/XmlBuilder)。
生成器模式的优点
思考
生成器的本质:分离整体构建算法和部件构造