生成器模式(Builder)是使用多个“小型”工厂来最终创建出一个完整对象。

    当我们使用Builder的时候,一般来说,是因为创建这个对象的步骤比较多,每个步骤都需要一个零部件,最终组合成一个完整的对象。

    我们仍然以Markdown转HTML为例,因为直接编写一个完整的转换器比较困难,但如果针对类似下面的一行文本:

    转换成HTML就很简单:

    • 如果以#开头,使用HeadingBuilder转换;
    • 如果以>开头,使用QuoteBuilder转换;
    • 如果以—-开头,使用HrBuilder转换;

    这个HtmlBuilder写出来如下:

    注意观察上述代码,HtmlBuilder并不是一次性把整个Markdown转换为HTML,而是一行一行转换,并且,它自己并不会将某一行转换为特定的HTML,而是根据特性把每一行都“委托”给一个去转换,最后,把所有转换的结果组合起来,返回给客户端。

    这样一来,我们只需要针对每一种类型编写不同的Builder。例如,针对以#开头的行,需要HeadingBuilder

    1. public class HeadingBuilder {
    2. public String buildHeading(String line) {
    3. int n = 0;
    4. while (line.charAt(0) == '#') {
    5. n++;
    6. line = line.substring(1);
    7. }
    8. }
    9. }

    注意:实际解析Markdown是带有状态的,即下一行的语义可能与上一行相关。这里我们简化了语法,把每一行视为可以独立转换。

    JavaMail的MimeMessage就可以看作是一个Builder模式,只不过Builder和最终产品合二为一,都是MimeMessage

    很多时候,我们可以简化Builder模式,以链式调用的方式来创建对象。例如,我们经常编写这样的代码:

    1. StringBuilder builder = new StringBuilder();
    2. builder.append(secure ? "https://" : "http://")
    3. .append("www.liaoxuefeng.com")
    4. .append("/")
    5. .append("?t=0");

    由于我们经常需要构造URL字符串,可以使用Builder模式编写一个URLBuilder,调用方式如下:

    从下载练习:使用Builder模式实现一个URLBuilder (推荐使用快速下载)

    生成器 - 图1