从零开始学设计模式(四)—建造者模式(Builder Pattern)

建造者模式(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中的现实例子

优缺点

优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

写在最后

建造者模式又称之为生成器模式,一般来说有三个角色:建造者、具体的建造者、监工角色,为了形象的说明这三个角色的结构和定义我们自己来设计一个程序实例。

我们假设要制作一份宣传文案,一份文案可以包含一个或多个文档,文档有三种类型:文字文档、图表文档、图片文档。根据不同类型文档的组合我们有不同类型的文案生成,如文字文案由纯文字文档组成,图表文案由图表文档和图片文档组成,混合文案由文字文档、图表文档、图片文档三者共同组成。

不同类型的文档由不同的书写工具书写,如文字文档由MicrosoftWord工具编写,图表文档由MicrosoftExcel工具编写,图片文档由PhotoShop工具编写。

按照上面的假设需求,我们首先设计程序类图如下:


image.png

接下来编写程序
步骤一:创建文档接口和编写工具接口

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)这将是最后一个创建型模式

码字不易,各位看官如果喜欢的话,请给点个喜欢❤️,关注下我。我将努力持续不断的为大家更新完此系列

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,710评论 0 14
  • 没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组...
    justCode_阅读 1,768评论 1 6
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,820评论 1 15
  • 【学习难度:★★★★☆,使用频率:★★☆☆☆】直接出处:建造者模式梳理和学习:https://github.com...
    BruceOuyang阅读 734评论 0 5
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139