生成器 Builder 生成器模式 也叫“建造者模式”是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
为什么要使用? 生成器模式的对象职责: 
分步骤创建复杂对象。 分阶段、分步骤的方法更适合多次运算结果类创建场景。在实际开发中,并非所有参数都能一次性准备好的,需要通过其他运算后才能得出。这时我们有 3 种方法可以实现:
构造函数: 如果参数很多,并且有许多不必要生成的参数,那么我们就需要根据各种情况,编写各种构造函数,或者在全参构造函数中为那些不必要的参数传值。代码臃肿、多余。
Setter 方法: 如果使用 Setter 方法,就需要用无参构造先创建对象,再一个个 Setter 属性,如果属性过多,而且要生成的对象多,也是很麻烦、冗余。
生成器模式: 生成器模式可以帮助你分步骤的创建一个复杂对象,虽然生成器模式本身会占用一定的资源。但如果要创建很多对象或创建的对象很复杂,这点资源也可以忽略不计。例如创建一个学生对象:
1 Student student = new  Student.Builder().name("张三" ).age(18 ).sex("男" ).build(); 
 
使用相同的创建代码生成不同类型和形式的对象。 不需要关系特定类型的建造者的具体算法实现。例如很多框架都会使用生成器模式去创建对象,我们在使用这些框架去创建某些对象时,不需要关注其内部逻辑,只需要着重关注它能给我们带来什么功能,这样能提高开发效率。
 
模式结构 
生成器  (Builder) 接口声明在所有类型生成器中通用的产品构造步骤。具体生成器  (Concrete Builders) 提供构造过程的不同实现。 具体生成器也可以构造不遵循通用接口的产品。产品  (Products) 是最终生成的对象。 由不同生成器构造的产品无需属于同一类层次结构或接口。主管  (Director) 类定义调用构造步骤的顺序, 这样你就可以创建和复用特定的产品配置。客户端  (Client) 必须将某个生成器对象与主管类关联。 一般情况下, 你只需通过主管类构造函数的参数进行一次性关联即可。 此后主管类就能使用生成器对象完成后续所有的构造任务。 但在客户端将生成器对象传递给主管类制造方法时还有另一种方式。 在这种情况下, 你在使用主管类生产产品时每次都可以使用不同的生成器。 
生成器模式的类图: 
生成器模式的顺序图: 
模式实现 该示例通过使用生成器模式分别创建复杂对象 HTML 页面和 Markdown 文档。
示例程序的类图 
代码实现 文档生成器 Builder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  interface  Builder           void  makeTitle (String title)           void  makeItems (String[] items)           void  makeContent (String content)  } 
HTML生成器 HTMLBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public  class  HTMLBuilder  implements  Builder      private  String html = "" ;          @Override      public  void  makeTitle (String title)           html += "<html><head><title>"  + title + "</title></head></html>"  + "\n" ;     }          @Override      public  void  makeItems (String[] items)           html += "<ul>"  + "\n" ;         for  (String item : items) {             html += "<li>"  + item + "</li>"  + "\n" ;         }         html += "</ul>"  + "\n" ;     }          @Override      public  void  makeContent (String content)           html += "<p>"  + content + "</p>"  + "\n" ;     }          public  String getResult ()           return  html;     } } 
Markdown文档生成器 MarkdownBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public  class  MarkdownBuilder  implements  Builder      private  String markdown = "" ;          @Override      public  void  makeTitle (String title)           markdown += "## "  + title + "\n" ;     }          @Override      public  void  makeItems (String[] items)           for  (String item : items) {             markdown += "* "  + item + "\n" ;         }     }          @Override      public  void  makeContent (String content)           markdown += content + "\n" ;     }          public  String getResult ()           return  markdown;     } } 
编写文档主管 Director 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  class  Director           private  Builder builder;          public  Director (Builder builder)           this .builder = builder;     }          public  void  construct ()           builder.makeTitle("这是一个标题" );         String[] items = {"无序列表1" , "无序列表2" , "无序列表3" };         builder.makeItems(items);         builder.makeContent("这是一段内容!" );     } } 
代码测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public  class  Test      public  static  void  main (String[] args)           System.out.println("用HTMLBuilder生成文档" );         HTMLBuilder htmlBuilder = new  HTMLBuilder();         Director director1 = new  Director(htmlBuilder);         director1.construct();         System.out.println(htmlBuilder.getResult());         System.out.println("----------------- 分割线 -----------------\n" );         System.out.println("用MarkdownBuilder生成文档" );         MarkdownBuilder markdownBuilder = new  MarkdownBuilder();         Director director2 = new  Director(markdownBuilder);         director2.construct();         System.out.println(markdownBuilder.getResult());     } } 
输出结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 用HTMLBuilder生成文档 <html > <head > <title > </title > </head > </html > <ul > <li > </li > <li > </li > <li > </li > </ul > <p > </p > ----------------- 分割线 ----------------- 用MarkdownBuilder生成文档 ## 这是一个标题 *  无序列表1*  无序列表2*  无序列表3这是一段内容! 
我们也可以直接跳过 Director,直接使用 Builder,或者在 Director 里编写多个方法,注入不同 Builder。
常用场景和解决方案 
需要生成的对象包含多个成员属性,使用生成器模式可以避免“重叠构造函数”和“繁琐 Setter”的出现。 
需要生成的对象的属性相互依赖,需要指定其生成顺序。 
对象的创建过程独立于创建该对象的类。 
需要隔离复杂对象的创建和使用,并使用相同的创建过程可以创建不同的产品。 
 
模式的优缺点 
优点 
缺点 
 
 
你可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。 
由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。 
 
生成不同形式的产品时, 你可以复用相同的制造代码。 
 
单一职责原则 。 你可以将复杂构造代码从产品的业务逻辑中分离出来。 
使用生成器模式的优势 
分离创建与使用,使用方不需要知道类的内部实现逻辑细节,通过统一接口调用,可以组合出不同类型的对象。 
满足开闭原则,每一个生成器都相对独立,可以很容易替换或新增,提高代码的可拓展性。 
自由地组合对象的创建过程,使用者可以使用少量代码灵活创建满足自己需求的对象。 
 
使用生成器模式的劣势 
使用范围有限。 
容易引起超大的类。 
增加代码行数。 
 
拓展知识 
生成器重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂 会马上返回产品, 生成器 则允许你在获取产品前执行一些额外构造步骤。 
你可以结合使用生成器和桥接模式:主管 类负责抽象工作,各种不同的生成器 负责实现 工作。 
抽象工厂、 生成器和原型都可以用单例模式来实现。 
设计时要谨记“只有不知道子类才能替换”。 
 
🔙 设计模式 
📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言! 
🔗参考文献: 
🌐 设计模式 –refactoringguru 
▶️ bilibili-趣学设计模式;黄靖锋. –拉勾教育 
📖 图解设计模式 /(日)结城浩著;杨文轩译. –北京:人民邮电出版社,2017.1