定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

类型:对象创建型模式

类图:

  • Builder:生成器接口,定义创建一个Product对象所需要的各个部件的操作。
  • ConcreteBuilder:具体的生成器实现,实现各个部件的创建,并负责组装Product对象的各个部件,同时还提供一个让用户获取组装完成后的产品对象的方法。
  • Director:指导者,也被称导向者,主要用来使用Builder接口,以一个统一的过程来构建所需要的Product对象。
  • Product:产品,表示被生成器构建的复杂对象,包含多个部件。

生成器模式示例代码

1、生成器接口定义的示例代码

/*** 生成器接口,定义创建一个产品对象所需的各个部件的操作* @author FX_SKY**/
public interface Builder {/*** 示意方法,构建某个部件*/public void buildPart();
}

2、具体生成器实现的示例代码

/*** 具体的生成器实现对象* @author FX_SKY**/
public class ConcreteBuilder implements Builder {private Product resultProduct;/*** 获取生成器最终构建的产品对象* @return*/public Product getResultProduct() {return resultProduct;}@Overridepublic void buildPart() {//构建某个部件的功能处理}}

3、相应的产品对象接口的示例代码

/*** 被构建的产品对象的接口* @author FX_SKY**/
public interface Product {//定义产品的操作
}

4、最后是指导者的实现示意,示例代码如下:

/*** 指导者,指导使用生成器的接口来构建产品对象* @author FX_SKY**/
public class Director {/*** 持有当前需要使用的生成器对象*/private Builder builder;/*** 构造方法,传人生成器对象* @param builder*/public Director(Builder builder) {this.builder = builder;}/*** 示意方法,指导生成器构建最终的产品对象*/public void construct(){//通过使用生成器接口来构建最终的产品对象builder.buildPart();}
}

应用场景-- 导出数据的应用框架

在讨论工厂方法模式的时候,提供了一个导出数据的应用框架。

对于导出数据的应用框架,通常在导出数据上,会有一些约束的方式,比如导出成文本格式、数据库备份形式、Excel格式、Xml格式等。

在工厂方法模式章节里面,讨论并使用工厂方法模式来解决了如何选择具体导出方式的问题,并没有涉及到每种方式具体如何实现。

换句话说,在讨论工厂方法模式的时候,并没有讨论如何实现导出成文本、Xml等具体格式,本章就来讨论这个问题。

对于导出数据的应用框架,通常对于具体的导出内容和格式是有要求的,加入现在有如下要求,简单描述一下:

  • 导出的文件,不管是什么格式,都分成3个部分,分别是文件头、文件体、文件尾。
  • 在文件头部分,需要描述如下信息:分公司或者门市编号、导出数据的日期。
  • 在文件体部分,需要描述如下信息:表名称,然后分条描述数据。
  • 在文件尾部分,需要描述如下信息:输出人。

1、下面将描述文件各个部分的数据对象定义出来

描述输出到文件头的内容的对象,示例代码如下:

/*** 描述输出到文件头的内容的对象* @author FX_SKY**/
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;}}

描述输出数据的对象,示例代码如下:

/*** 描述输出数据的对象* @author FX_SKY**/
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;}}

描述输出到文件尾的内容的对象,示例代码如下:

/*** 描述输出到文件尾的内容的对象* @author FX_SKY**/
public class ExportFooterModel {/*** 输出人*/private String exportUser;public String getExportUser() {return exportUser;}public void setExportUser(String exportUser) {this.exportUser = exportUser;}}

2、定义Builder接口,主要是把导出各种格式文件的处理过程的步骤定义出来,每个步骤负责构建最终导出文件的一部分。示例代码如下:

/*** 生成器接口,定义创建一个输出文件对象所需的各个部件的操作* @author FX_SKY**/
public interface Builder {/*** 构建输出文件的Header部分* @param ehm*/public void buildHeader(ExportHeaderModel ehm);/*** 构建输出文件的Body部分* @param mapData*/public void buildBody(Map<String,List<ExportDataModel>> mapData);/*** 构建输出文件的Footer部分* @param efm*/public void buildFooter(ExportFooterModel efm);
}

3、具体的生成器实现。

导出到文本文件的的生成器实现。示例代码如下:

/*** 实现导出文件到文本文件的生成器对象* @author FX_SKY**/
public class TxtBuilder implements Builder {/*** 用来记录构建的文件的内容,相当于产品*/private StringBuffer buffer = new StringBuffer();@Overridepublic void buildHeader(ExportHeaderModel ehm) {buffer.append(ehm.getDepId()+","+ehm.getExportDate()+"\n");}@Overridepublic void buildBody(Map<String, List<ExportDataModel>> mapData) {for(String tablName : mapData.keySet()){//先拼接表名buffer.append(tablName+"\n");//然后循环拼接具体数据for(ExportDataModel edm : mapData.get(tablName)){buffer.append(edm.getProductId()+","+edm.getPrice()+","+edm.getAmount()+"\n");}}}@Overridepublic void buildFooter(ExportFooterModel efm) {buffer.append(efm.getExportUser());}public StringBuffer getResult(){return buffer;}}

导出到Xml文件的的生成器实现。示例代码如下:

/*** 实现导出文件到Xml文件的生成器对象* @author FX_SKY**/
public class XmlBuilder implements Builder {/*** 用来记录构建的文件的内容,相当于产品*/private StringBuffer buffer = new StringBuffer();@Overridepublic void buildHeader(ExportHeaderModel ehm) {buffer.append("<?xml version='1.0' encoding='UTF-8'?>\n");buffer.append("<Report>\n");buffer.append("\t<Header>\n");buffer.append("\t\t<DepId>"+ehm.getDepId()+"</DepId>\n");buffer.append("\t\t<ExportDate>"+ehm.getExportDate()+"</ExportDate>\n");buffer.append("\t</Header>\n");}@Overridepublic void buildBody(Map<String, List<ExportDataModel>> mapData) {buffer.append("\t<Body>\n");for(String tablName : mapData.keySet()){//先拼接表名buffer.append("\t\t<Datas TableName=\""+tablName+"\">\n");//然后循环拼接具体数据for(ExportDataModel edm : mapData.get(tablName)){buffer.append("\t\t\t<Data>\n");buffer.append("\t\t\t\t<ProductId>"+edm.getProductId()+"</ProductId>\n");buffer.append("\t\t\t\t<Price>"+edm.getPrice()+"</Price>\n");buffer.append("\t\t\t\t<Amount>"+edm.getAmount()+"</Amount>\n");buffer.append("\t\t\t</Data>\n");}buffer.append("\t\t</Datas>\n");}buffer.append("\t</Body>\n");}@Overridepublic void buildFooter(ExportFooterModel efm) {buffer.append("\t<Footer>\n");buffer.append("\t\t<ExportUser>"+efm.getExportUser()+"</ExportUser>\n");buffer.append("\t</Footer>\n");buffer.append("</Report>\n");}public StringBuffer getResult(){return buffer;}}

4、指导者。有了具体的生成器实现后,需要由指导者来指导它进行具体的产品构建。示例代码如下:

/*** 指导者,指导使用生成器的接口来构建输出的文件对象* * @author FX_SKY* */
public class Director {/*** 持有当前需要的使用的生成器对象*/private Builder builder;/*** 构造方法,传入生成器对象* * @param builder*/public Director(Builder builder) {this.builder = builder;}public void construct(ExportHeaderModel ehm,Map<String, List<ExportDataModel>> mapData, ExportFooterModel efm) {//1.先构建Headerbuilder.buildHeader(ehm);//2.然后构建Bodybuilder.buildBody(mapData);//3.再构建Footerbuilder.buildFooter(efm);}
}

5、客户端测试代码如下:

public class Client {/*** @param args*/public static void main(String[] args) {//准备测试数据ExportHeaderModel ehm = new ExportHeaderModel();ehm.setDepId("一分公司");ehm.setExportDate("2010-05-18");Map<String, List<ExportDataModel>> mapData = new HashMap<String, List<ExportDataModel>>();List<ExportDataModel> col = new ArrayList<ExportDataModel>();ExportDataModel edm1 = new ExportDataModel();edm1.setProductId("产品001号");edm1.setPrice(100);edm1.setAmount(80);ExportDataModel edm2 = new ExportDataModel();edm2.setProductId("产品002号");edm2.setPrice(120);edm2.setAmount(280);ExportDataModel edm3 = new ExportDataModel();edm3.setProductId("产品003号");edm3.setPrice(320);edm3.setAmount(380);col.add(edm1);col.add(edm2);col.add(edm3);mapData.put("销售记录表", col);ExportFooterModel efm = new ExportFooterModel();efm.setExportUser("张三");//测试输出到文本文件TxtBuilder txtBuilder = new TxtBuilder();//创建指导者对象Director director = new Director(txtBuilder);director.construct(ehm, mapData, efm);//把要输出的内容输出到控制台看看System.out.println("输出到文本文件的内容:"+txtBuilder.getResult().toString());XmlBuilder xmlBuilder = new XmlBuilder();Director director2 = new Director(xmlBuilder);director2.construct(ehm, mapData, efm);//把要输出的内容输出到控制台看看System.out.println("输出到Xml文件的内容:"+xmlBuilder.getResult().toString());}}

生成器模式的功能

生成器模式的主要功能是构建复杂的产品,而且是细化的,分步骤的构建产品,也就是生成器模式重在一步一步解决构造复杂对象的问题。如果仅仅这么认知生成器模式的功能是不够的。

更为重要的是,这个构建的过程是统一的、固定不变的,变化的部分放到生成器部分了,只要配置不同的生成器,那么同样的构建过程,就能构建出不同的产品来。

使用生成器模式构建复杂的对象

考虑这样的一个实际应用,Android图片异步加载框架,需要要创建图片加载配置的对象,里面很多属性的值都有约束,要求创建出来的对象是满足这些约束规则的。约束规则比如,线程池的数量不能小于2个、内存图片缓存的大小不能为负值等等。

要想简洁直观、安全性好,有具有很好的扩展性地创建这个对象的话,一个较好的选择就是使用Builder模式,把复杂的创建过程通过Builder来实现。

采用Builder模式来构建复杂的对象,通常会对Builder模式进行一定的简化,因为目标明确,就是创建某个复杂对象,因此做适当简化会使程序更简洁。大致简化如下:

  • 由于是用Builder模式来创建某个对象,因此就没有必要再定义一个Builder接口,直接提供一个具体的构建器类就可以了。
  • 对于创建一个负责的对象,可能会有很多种不同的选择和步骤,干脆去掉“指导者”,把指导者的功能和Client的功能合并起来,也就是说,Client就相当于指导者,它来指导构建器类去构建需要的复杂对象。
public final class ImageLoaderConfiguration {final Executor taskExecutor;final int memoryCacheSize;final int threadPoolSize;final int threadPriority;final boolean writeLogs;private ImageLoaderConfiguration(final Builder builder) {taskExecutor = builder.taskExecutor;threadPoolSize = builder.threadPoolSize;threadPriority = builder.threadPriority;memoryCacheSize = builder.memoryCacheSize;writeLogs = builder.writeLogs;}/*** Builder for {@link ImageLoaderConfiguration}** @author Sergey Tarasevich (nostra13[at]gmail[dot]com)*/public static class Builder {public static final int DEFAULT_THREAD_POOL_SIZE = 3;public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 1;private int memoryCacheSize = 0;private Executor taskExecutor = null;private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;private int threadPriority = DEFAULT_THREAD_PRIORITY;private boolean writeLogs = false;public Builder() {}public Builder taskExecutor(Executor executor) {if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY) {}this.taskExecutor = executor;return this;}public Builder threadPoolSize(int threadPoolSize) {this.threadPoolSize = threadPoolSize;return this;}public Builder threadPriority(int threadPriority) {if (threadPriority < Thread.MIN_PRIORITY) {this.threadPriority = Thread.MIN_PRIORITY;} else {if (threadPriority > Thread.MAX_PRIORITY) {this.threadPriority = Thread.MAX_PRIORITY;} else {this.threadPriority = threadPriority;}}return this;}public Builder memoryCacheSize(int memoryCacheSize) {if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number");this.memoryCacheSize = memoryCacheSize;return this;}public Builder writeDebugLogs() {this.writeLogs = true;return this;}/** Builds configured {@link ImageLoaderConfiguration} object */public ImageLoaderConfiguration build() {initEmptyFieldsWithDefaultValues();return new ImageLoaderConfiguration(this);}private void initEmptyFieldsWithDefaultValues() {if (taskExecutor == null) {}}}
}

客户端调用示例代码如下:

public class Client {/*** @param args*/public static void main(String[] args) {ImageLoaderConfiguration  config = new ImageLoaderConfiguration.Builder().taskExecutor(Executors.newCachedThreadPool()).threadPoolSize(3).threadPriority(Thread.MIN_PRIORITY + 3).memoryCacheSize(1024*16).build();}}

生成器模式的优点

松散耦合

生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现分松散耦合,从而使得构建算法可以复用,而具体产品表现也可以很灵活地、方便地扩展和切换。

可以很容易的改变产品的内部表示

在生成器模式中,由于Builder对象只是提供接口给Director使用,那么具体部件创建和装配方式是被Builder接口隐藏了的,Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换Builder接口的具体实现即可,不用管Director,因此变得很容易。

更好的复用性

生成器模式很好的实现构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用。

生成器模式的本质:分离整体构建算法和部件构造。

虽然在生成器模式的整体构建算法中,会一步一步引导Builder来构建对象,但这并不是说生成器主要就是用来实现分步骤构建对象的。生成器模式的重心还是在于分离整体构建算法和部件构造,而分步骤构建对象不过是整体构建算法的一个简单表现,或者说是一个附带产物。

何时选用生成器模式

建议在以下情况中选用生成器模式。

  • 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时。
  • 如果同一个构建过程有着不同的表示时。

JAVA设计模式 — 生成器模式(Builder)相关推荐

  1. C#设计模式——生成器模式(Builder Pattern)

    一.概述 在软件系统中,有时候面临着复杂的对象创建,该对象由一定算法构成的子对象组成,由于需求变化,这些子对象会经常变换,但组合在一起的算法却是稳定的.生成器模式可以处理这类对象的构建,它提供了一种封 ...

  2. Java 设计模式——建造者模式(Builder Pattern)

    前言 一.简介 ​二.实现方式 三.常见第一种方式 (1)一般有以下几个角色 (2)举个例子 (3)具体步骤 (4)具体代码 三.第二种方式 (1)主要有三个角色:抽象建造者.具体建造者.产品 (2) ...

  3. 重学Java设计模式-创建者模式-建造者模式

    重学Java设计模式-创建者模式-建造者模式 内容摘自:重学 Java 设计模式:实战建造者模式「各项装修物料组合套餐选配场景」 | bugstack 虫洞栈 建造者模式介绍 图片来自:https:/ ...

  4. Java 设计模式 --- Template 模式 Java Template 模式 Java 模板设计模式

    Java 设计模式 --- Template 模式 Java Template 模式 Java 模板设计模式 一.概述 模板设计模式: 父类定义通用抽象的功能方法,子类负责具体的实现. 本文将以一个通 ...

  5. 说说设计模式~建造者模式(Builder)

    建造者模式是我的"设计模式"里创建型模式里的最后一篇,这种模式在实现中,很多架构都用到了,如MVC,MVP,MVVM,它们都是有建造者模式的精髓的,即,创建与表现分享,我们的MVC ...

  6. Java 设计模式——状态模式

    概述 很多人在说状态模式的时候总拿策略模式来进行对比,可能他们的类图会有一点类似,可我却不认为他们有多么相像.你可以阅读<Java设计模式--策略模式>这篇博客,并与本文对比,以找到蛛丝马 ...

  7. Java设计模式-工厂模式(3)抽象工厂模式

    在Java设计模式-工厂模式(2)工厂方法模式 我们知道了工厂方法模式解决了简单工厂模式中的缺陷,做到了满足开闭原则,但是时代是进步的,进而又产生新的问题,工厂难道只能生产一种东西吗.我们所见到的工厂 ...

  8. Java设计模式-工厂模式(2)工厂方法模式

    在Java设计模式-工厂模式(1)简单工厂模式 中我们介绍了简单工厂模式,提到了简单工厂模式违背了开闭原则,而"工厂方法模式"是对简单工厂模式的进一步抽象化,其好处是可以使系统在不 ...

  9. Java设计模式-工厂模式(1)简单工厂模式

    Java设计模式-工厂模式(1)简单工厂模式 一.前言 1)例子 2)类图关系 3)代码实现 二.简单工厂模式 2.1.概述: 2.2.类图关系: 2.3.代码修改: 2.4.优缺点 2.5.扩展-简 ...

  10. java设计模式代理模式_Java中的代理设计模式

    java设计模式代理模式 代理对象或代理对象为另一个对象提供占位符,以控制对该对象的访问. 代理充当原始对象的轻量级版本或简化版本. 它支持与原始对象相同的操作,但可以将那些请求委托给原始对象以实现它 ...

最新文章

  1. 利用统计滤波方法去除空中漂浮物 以及去噪
  2. 独家|图说Pandas中旋转和重塑函数
  3. 高校毕业生:今年“太惨了”,网友:更惨的可能还在后头!
  4. Android存储Json到本地,和读取本地Json
  5. interactive_timeout和wait_timeout之间的区别
  6. jdk8 string::_JDK 12的String :: transform方法的简要但复杂的历史
  7. 面试必备:CAS无锁机制
  8. aes加密 js_吊打SRC的加密传输实现SQLi挖掘
  9. 百度编辑器UEditor修改成支持物理路径
  10. PTA编程总结3—抓老鼠啊~亏了还是赚了?
  11. MATLAB公式希腊字母表
  12. numpy创建单位矩阵和对角矩阵
  13. 20年进入下半赛程,你变强了吗?
  14. 400. 第 N 位数字【我亦无他唯手熟尔】
  15. 打开mysql 的时候报错_关于mysql的启动报错处理
  16. 粉丝说SpringBoot集成validation校验参数有坑,我试了试
  17. Axure 初学者必看:自学 Axure 需要花多长时间?
  18. M1版本Photoshop2021原生ARM完整安装方法下载(适配M1芯片全系Mac教程)
  19. 基于STM32F103系列单片机四路定时器电机编码器模式配置过程附源码
  20. 原理 一篇文章通透理解序列号实现原理

热门文章

  1. 0基础可以考CPDA数据分析师证书吗
  2. 毕业设计 大数据房价数据分析及可视化 - python 房价分析
  3. 台式电脑连不上wifi怎么办
  4. java反编译luyten使用
  5. 系统建模与仿真 - 电子书下载(高清版PDF格式+EPUB格式)
  6. 软考初级程序员---题目(二)
  7. afm原子力分析软件_AFM数据处理软件|原子力显微镜配套数据处理软件 nanoscope analysis1.8 官方版 - 极光站...
  8. java 生成中文字符乱码,java汉字乱码的原因与解决方法
  9. 游戏开发需要懂几种语言?
  10. Ubuntu删除用户和卸载服务命令