跳到主要内容

建造者模式(Builder Pattern)

引言

现在一般大型的业务系统中的消息通知的形式都会有多种,比如短信、站内信、钉钉通知、邮箱等形式。虽然信息内容相同,但是展现形式缺不同。如短信使用的是纯文本的形式,钉钉使用的一般是Markdown的形式,而邮箱则使用HTML形式。那如何处理这种差异呢?

建造者模式就是一个很好的选择,使用不同的建造者,来构造不同的消息。

定义及实现

建造者模式(Builder Pattern)是一种创建型设计模式,它可以让你构建复杂对象时更加灵活和可控。这种模式的主要目的是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

在软件开发中,我们经常会遇到一些复杂的对象,这些对象通常由多个部分组成,而且每个部分的创建和组合可能需要花费大量的时间和精力。为了解决这个问题,我们可以使用建造者模式来简化对象的构建过程。

建造者模式的核心思想是将复杂对象的构建过程分解为多个简单的步骤,每个步骤负责创建一个特定的部分。这些步骤被封装在一个单独的类中,称为“建造者”(Builder)。通过这种方式,我们可以逐步构建复杂对象,同时保持代码的可读性和可维护性。

定义

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

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

结构

建造者模式结构
建造者模式结构

很多书或者博文中介绍建造者模式的时候都说需要有一个 Director 类,然后在这个类中完成构建过程。这种情况适用于只需要顺序调用Builder的各个方法,即可完成构建过程的情况,或者说Builder的各个方法无参。

关于这种结构更多细节可以参考:https://refactoring.guru/design-patterns/builder

代码实现

具体代码中,使用 AbstractMessageBuilder 抽象 titleaddLine 方法,因为在3个Builder中,这两个方法的处理逻辑是一致的,不同的只有 build 方法。

@Data
public class Message {

private String title;

private String content;
}

public interface MessageBuilder {

void title(String title);

void addLine(String label, String desc);

Message build();
}

@Getter
public class Line {

private final String label;

private final String desc;

public Line(String label, String desc) {
this.label = label;
this.desc = desc;
}
}

public abstract class AbstractMessageBuilder implements MessageBuilder {

protected String title;

protected List<Line> lines = new ArrayList<>();

@Override
public void title(String title) {
this.title = title;
}

@Override
public void addLine(String label, String desc) {
this.lines.add(new Line(label, desc));
}
}

public class PlainTextMessageBuilder extends AbstractMessageBuilder{

@Override
public Message build() {
Message message = new Message();
message.setTitle(this.title);

StringBuilder sb = new StringBuilder();
for (Line line : this.lines) {
sb.append(line.getLabel()).append(":").append(line.getDesc()).append("\n");
}
message.setContent(sb.toString());
return message;
}
}

public class MarkdownMessageBuilder extends AbstractMessageBuilder{

@Override
public Message build() {
Message message = new Message();
message.setTitle(this.title);

StringBuilder sb = new StringBuilder();
for (Line line : this.lines) {
sb.append("**").append(line.getLabel()).append("**").append(":").append(line.getDesc()).append("\n\n");
}
message.setContent(sb.toString());
return message;
}
}

public class HtmlMessageBuilder extends AbstractMessageBuilder{

@Override
public Message build() {
Message message = new Message();
message.setTitle(this.title);

StringBuilder sb = new StringBuilder();
sb.append("<h2>").append(this.title).append("</h2>").append("\n");
for (Line line : this.lines) {
sb.append("<p>");
sb.append("<b>").append(line.getLabel()).append("</b>").append(":").append(line.getDesc());
sb.append("</p>").append("\n");
}
message.setContent(sb.toString());
return message;
}
}

public class Main {

public static void main(String[] args) {
MessageBuilder messageBuilder = new PlainTextMessageBuilder();

messageBuilder.title("服务异常提醒");
messageBuilder.addLine("服务", "user-service");
messageBuilder.addLine("接口名称", "获取用户详情");
messageBuilder.addLine("异常", " java.lang.NullPointerException");

Message message = messageBuilder.build();
System.out.println(message.getTitle());
System.out.println(message.getContent());
}
}

java

PlainTextMessageBuilder的输出结果

服务异常提醒
服务:user-service
接口名称:获取用户详情
异常: java.lang.NullPointerException

MarkdownMessageBuilder的输出结果

服务异常提醒
**服务**:user-service

**接口名称**:获取用户详情

**异常**: java.lang.NullPointerException

显示效果如下

服务:user-service

接口名称:获取用户详情

异常: java.lang.NullPointerException


HtmlMessageBuilder的输出结果

服务异常提醒
<h2>服务异常提醒</h2>
<p><b>服务</b>:user-service</p>
<p><b>接口名称</b>:获取用户详情</p>
<p><b>异常</b>: java.lang.NullPointerException</p>

显示效果:

服务异常提醒

服务:user-service

接口名称:获取用户详情

异常: java.lang.NullPointerException


实际应用

建造者模式在许多开源框架和库中得到了广泛应用,它主要用于创建复杂对象,特别是当对象的构建过程需要很多步骤或者需要不同表示时。以下是一些在开源框架中应用建造者模式的实例:

在Spring框架中,BeanDefinitionBuilder 用于构建 BeanDefinition 对象。通过提供一个流畅的接口,开发者可以逐步构建出复杂的bean定义。

RootBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class)
.addPropertyValue("propertyName", "propertyValue")
.getBeanDefinition();
java

lombok中的@Builder也是建造者模式的应用。

另外还有Okhttp的Request.Builder和Response.Builder等类也使用了建造者模式

总结

总的来说,建造者模式提供了一种优雅的方式来创建复杂对象,其核心在于隐藏了复杂的构建过程。它通过封装、分离创建和使用过程、提高扩展性和灵活性,以及控制构建过程,使得代码更加模块化和易于管理。这些优点使得建造者模式在软件设计中被广泛采用。

在一些开源的框架中只要结尾是 Builder 的类,一般都使用了建造者模式。