建造者模式(Builder Pattern)
建造者模式使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式也属于创建型模式,它提供了一种创建对象的最佳方式。
一个Builder 类会一步一步构造最终的对象。该Builder 类是独立于其他对象的
难度系统:中级
提出者:Gang Of Four
意图
将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
解释
现实世界的例子
想象一个角色扮演游戏的角色生成器。最简单的选择是让电脑为你创建角色。但是如果你想选择角色细节,如职业、性别、发色等。当你所有选择一步一步都选定好时,角色生成器也就逐步生成了一个角色,这一过程就是建造者模式创建对象的过程
简而言之
允许你创建不同风格的对象,同时避免构造函数污染。当一个对象可能有多种风格时很有用。或者当创建对象涉及很多步骤时
维基百科说
The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern(建造者模式是一种对象创建软件设计模式,旨在找到伸缩构造器反模式的解决方案)
说到这里,让我补充一下伸缩构造函数反模式是什么。在这一点或其他方面,我们可能都见过类似下面这样的构造函数
public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) {
}
正如你可以看到的未来那样,这个构造函数参数的数量会很快失控,并且很难理解参数的排列顺序和组合。此外,如果你在未来增加更多选项,这个参数列表可能会继续增长。这称为伸缩构造函数反模式
程序代码示例
上面的N参数构造函数示例,明智的选择是使用建造者模式。首先,我们有我们想要创造的英雄
public final class Hero {
private final Profession profession;
private final String name;
private final HairType hairType;
private final HairColor hairColor;
private final Armor armor;
private final Weapon weapon;
private Hero(Builder builder) {
this.profession = builder.profession;
this.name = builder.name;
this.hairColor = builder.hairColor;
this.hairType = builder.hairType;
this.weapon = builder.weapon;
this.armor = builder.armor;
}
}
然后我们设计建造者
public static class Builder {
private final Profession profession;
private final String name;
private HairType hairType;
private HairColor hairColor;
private Armor armor;
private Weapon weapon;
public Builder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null");
}
this.profession = profession;
this.name = name;
}
public Builder withHairType(HairType hairType) {
this.hairType = hairType;
return this;
}
public Builder withHairColor(HairColor hairColor) {
this.hairColor = hairColor;
return this;
}
public Builder withArmor(Armor armor) {
this.armor = armor;
return this;
}
public Builder withWeapon(Weapon weapon) {
this.weapon = weapon;
return this;
}
public Hero build() {
return new Hero(this);
}
}
最后我们可以这样来建造Hero:
Hero mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build();
应用场景
当遇到如下的情况你应该考虑使用建造者模式:
- 创建复杂对象的算法应该独立于组成对象的部件以及它们是如何组装的
- 构建过程必须允许对构建的对象进行不同的表示
Java中的现实例子
- java.lang.StringBuilder
- java.nio.ByteBuffer 还有其他类似的buffers 比如 FloatBuffer, IntBuffer 等等.
- java.lang.StringBuffer
- All implementations of java.lang.Appendable
- Apache Camel builders
优缺点
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
写在最后
建造者模式又称之为生成器模式,一般来说有三个角色:建造者、具体的建造者、监工角色,为了形象的说明这三个角色的结构和定义我们自己来设计一个程序实例。
我们假设要制作一份宣传文案,一份文案可以包含一个或多个文档,文档有三种类型:文字文档、图表文档、图片文档。根据不同类型文档的组合我们有不同类型的文案生成,如文字文案由纯文字文档组成,图表文案由图表文档和图片文档组成,混合文案由文字文档、图表文档、图片文档三者共同组成。
不同类型的文档由不同的书写工具书写,如文字文档由MicrosoftWord工具编写,图表文档由MicrosoftExcel工具编写,图片文档由PhotoShop工具编写。
按照上面的假设需求,我们首先设计程序类图如下:
接下来编写程序
步骤一:创建文档接口和编写工具接口
public interface Document {
/**
*
* @return 文档名称
*/
String name();
/**
*
* @return 文档类型
*/
String type();
/**
*
* @return 书写工具
*/
WriteTool writeTool();
}
public interface WriteTool {
/**
*
* @return 返回书写工具[名称]+"write"
*/
String write();
}
步骤二:编写WriteTool接口的实现类
public class MicrosoftWord implements WriteTool{
@Override
public String write() {
return "MicrosoftWord write";
}
}
public class MicrosoftExcel implements WriteTool {
@Override
public String write() {
return "MicrosoftExcel write";
}
}
public class PhotoShop implements WriteTool{
@Override
public String write() {
return "PhotoShop write";
}
}
步骤三:编写Document接口的实现类
public class Chart implements Document {
@Override
public String name() {
return "chart document";
}
@Override
public String type() {
return "table";
}
@Override
public WriteTool writeTool() {
return new MicrosoftExcel();
}
}
public class Image implements Document {
@Override
public String name() {
return "image document";
}
@Override
public String type() {
return "image";
}
@Override
public WriteTool writeTool() {
return new PhotoShop();
}
}
public class Word implements Document{
@Override
public String name() {
return "word document";
}
@Override
public String type() {
return "text";
}
@Override
public WriteTool writeTool() {
return new MicrosoftWord();
}
}
步骤四:编写建造者CopyWriter类
/**
* 不同的文案包含一些不同类型的文档
* 定义建造对象的方式方法
*/
public class CopyWriter {
//包含的文档
private List<Document> documents = new ArrayList<>();
//名字
private String name;
//文案类型 文字 图表 混合
private String type;
public CopyWriter(String name,String type){
this.name = name;
this.type = type;
}
//添加文档
public CopyWriter addDocument(Document document) {
if (null == document){
throw new IllegalArgumentException("documnet can not be null");
}
this.documents.add(document);
return this;
}
public String name(){
return this.name;
}
public String getType(){
return this.type;
}
//展示文案包含的文档信息
public void showDocuments(){
for (Document doc:documents)
{
System.out.print("name:"+doc.name());
System.out.print(" type:"+doc.type());
System.out.println(" writeTool:"+doc.writeTool().write());
}
}
}
步骤五:编写监工CopyWriterBuilder
//将一个复杂对象的构建过程与其表示相分离
public class CopyWriterBuilder {
/**
* 准备文本类型的文案
* @return
*/
public CopyWriter prepareTextCopyWriter(){
CopyWriter copyWriter = new CopyWriter("TextCopyWriter","text");
//文本类型的文案只需要准备文字文档即可
copyWriter.addDocument(new Word());
return copyWriter;
}
/**
* 准备图表类型的文案
* @return
*/
public CopyWriter prepareTableCopyWriter(){
CopyWriter copyWriter = new CopyWriter("TableCopyWriter","table");
//图表类型的文案需要准备图表文档和图片文档
copyWriter.addDocument(new Chart()).addDocument(new Image());
return copyWriter;
}
/**
* 准备混合类型的文案 包含文本和图表
* @return
*/
public CopyWriter prepareMixCopyWriter(){
CopyWriter copyWriter = new CopyWriter("MixCopyWriter","Mix");
//图表类型的文案需要准备图表文档、图片文档、文字文档
copyWriter.addDocument(new Chart()).addDocument(new Image()).addDocument(new Word());
return copyWriter;
}
}
步骤六:最后编写使用者
public class App {
public static void main(String[] args){
CopyWriterBuilder builder = new CopyWriterBuilder();
CopyWriter txtCopyWriter = builder.prepareTextCopyWriter();
System.out.println(txtCopyWriter.name());
txtCopyWriter.showDocuments();
System.out.println("---------------------------------");
CopyWriter tableCopyWriter = builder.prepareTableCopyWriter();
System.out.println(tableCopyWriter.name());
tableCopyWriter.showDocuments();
System.out.println("---------------------------------");
CopyWriter mixCopyWriter = builder.prepareMixCopyWriter();
System.out.println(mixCopyWriter.name());
mixCopyWriter.showDocuments();
}
}
运行App输出结果如下:
TextCopyWriter
name:word document type:text writeTool:MicrosoftWord write
---------------------------------
TableCopyWriter
name:chart document type:table writeTool:MicrosoftExcel write
name:image document type:image writeTool:PhotoShop write
---------------------------------
MixCopyWriter
name:chart document type:table writeTool:MicrosoftExcel write
name:image document type:image writeTool:PhotoShop write
name:word document type:text writeTool:MicrosoftWord write
下一章节我将介绍原型模式(Prototype Pattern)这将是最后一个创建型模式
码字不易,各位看官如果喜欢的话,请给点个喜欢️,关注下我。我将努力持续不断的为大家更新完此系列