阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

生成器

30次阅读
没有评论

共计 2907 个字符,预计需要花费 8 分钟才能阅读完成。

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

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

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

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

# this is a heading

转换成 HTML 就很简单:

<h1>this is a heading</h1>

因此,我们把 Markdown 转 HTML 看作一行一行的转换,每一行根据语法,使用不同的转换器:

  • 如果以 # 开头,使用 HeadingBuilder 转换;
  • 如果以 > 开头,使用 QuoteBuilder 转换;
  • 如果以 --- 开头,使用 HrBuilder 转换;
  • 其余使用 ParagraphBuilder 转换。

这个 HtmlBuilder 写出来如下:

public class HtmlBuilder {private HeadingBuilder headingBuilder = new HeadingBuilder();
    private HrBuilder hrBuilder = new HrBuilder();
    private ParagraphBuilder paragraphBuilder = new ParagraphBuilder();
    private QuoteBuilder quoteBuilder = new QuoteBuilder();

    public String toHtml(String markdown) {StringBuilder buffer = new StringBuilder();
        markdown.lines().forEach(line -> {if (line.startsWith("#")) {buffer.append(headingBuilder.buildHeading(line)).append('\n');
            } else if (line.startsWith(">")) {buffer.append(quoteBuilder.buildQuote(line)).append('\n');
            } else if (line.startsWith("---")) {buffer.append(hrBuilder.buildHr(line)).append('\n');
            } else {buffer.append(paragraphBuilder.buildParagraph(line)).append('\n');
            }
        });
        return buffer.toString();}
}

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

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

public class HeadingBuilder {public String buildHeading(String line) {int n = 0;
        while (line.charAt(0) == '#') {
            n++;
            line = line.substring(1);
        }
        return String.format("<h%d>%s</h%d>", n, line.strip(), n);
    }
}

注意

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

可见,使用 Builder 模式时,适用于创建的对象比较复杂,最好一步一步创建出“零件”,最后再装配起来。

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

Multipart multipart = new MimeMultipart();
// 添加 text:
BodyPart textpart = new MimeBodyPart();
textpart.setContent(body, "text/html;charset=utf-8");
multipart.addBodyPart(textpart);
// 添加 image:
BodyPart imagepart = new MimeBodyPart();
imagepart.setFileName(fileName);
imagepart.setDataHandler(new DataHandler(new ByteArrayDataSource(input, "application/octet-stream")));
multipart.addBodyPart(imagepart);

MimeMessage message = new MimeMessage(session);
// 设置发送方地址:
message.setFrom(new InternetAddress("[email protected]"));
// 设置接收方地址:
message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));
// 设置邮件主题:
message.setSubject("Hello", "UTF-8");
// 设置邮件内容为 multipart:
message.setContent(multipart);

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

StringBuilder builder = new StringBuilder();
builder.append(secure ? "https://" : "http://")
       .append("www.liaoxuefeng.com")
       .append("/")
       .append("?t=0");
String url = builder.toString();

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

String url = URLBuilder.builder() // 创建 Builder
        .setDomain("www.liaoxuefeng.com") // 设置 domain
        .setScheme("https") // 设置 scheme
        .setPath("/") // 设置路径
        .setQuery(Map.of("a", "123", "q", "K&R")) // 设置 query
        .build(); // 完成 build

练习

使用 Builder 模式实现一个 URLBuilder。

下载练习

小结

Builder 模式是为了创建一个复杂的对象,需要多个步骤完成创建,或者需要多个零件组装的场景,且创建过程中可以灵活调用不同的步骤或组件。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计2907字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中