一、描述 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。
建造者模式又称生成器模式,属于创建型模式 ,它提供了一种创建对象的最佳方式。
1、描述一 建造者模式 Builder 所构建的对象一定是庞大而复杂的,并且一定是按照既定的制造工序 将组件组装起来的。
建造者模式又称为生成器模式,主要用于对复杂对象的构建、初始化,它可以将多个简单的组件对象按顺序一步步组装起来,最终构建成一个复杂的成品对象。
与工厂系列模式不同的是,建造者模式的主要目的在于把烦琐的构建过程从不同对象中抽离出来 ,使其脱离并独立于产品类与工厂类,最终实现用同一套标准的制造工序能够产出不同的产品。
建造步骤的重要性:建造者的制造过程不仅要分步完成,还要按照顺序进行,所以建造者的各制造步骤与逻辑都应该被抽离出来独立于数据模型
关于建造步骤,暂时理解的是可选,就目前看到的源码中,还没有看到需要建造步骤的建造者。另外,需要有构建顺序的建造者会需要一个 director 来定义构建顺序,builder (建造者、生成器)负责构建目标对象,而 director(导向类)负责指定构建顺序。
2、描述二 建造者模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
在以下情况使用建造者模式 :
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
当构造过程必须允许被构造的对象有不同的表示时
需要有:
builder 抽象接口
实现 builder 抽象接口的类
director,导向类
product,被构造的复杂对象
效果:
可以改变一个产品的内部表示
将构造代码和表示代码分开
可对构造过程进行更精细的控制
建造者模式与抽象工厂模式(Abstract Factory)很相似,因为他们都可以创建复杂对象,主要的区别是建造者模式着重于一步步构造一个复杂对象 。而抽象工厂模式着重于多个系列的产品对象(点单的或是复杂的)。建造者模式在最后一步返回产品,而抽象工厂产品是立即返回的。
3、描述三 使用构造方法,不能很好得扩展到有很多可选参数的情景。比如某一个对象,有几个必须属性,还有很多个可选属性。可以选择使用可伸缩构造方法模式 (telescoping constructor),在这种模式中,首先提供一个只有必须参数的构造方法,接着提供增加了一个可选参数的构造函数,然后提供增加了两个可选参数的构造函数,等等,最终在构造函数中包含所有必需和可选参数。
因为可选参数的排列组合有很多种情况,根据 Java 的语言特性,方法重载的限制,构造方法并不能覆盖所有情况。使用伸缩构造方法在创建对象时,可能会需要很多不想设置的参数,但是又不得不为它们传递一个值。当参数较少时可能看起来并不那么糟糕,但随着参数数量的增加,它很快就会失控。
可伸缩构造方法模式是有效的,但是当有很多参数时,很难编写客户端代码,而且很难读懂它 。coder 也不容易知道各个参数具体是什么意思,只能仔细地去数参数才能知道,并且如果一不小心赋值错误,编译器并不会报错,但是在程序运行时会出现错误的行为。
除了使用构造方法外,还可以选择使用 setter 来为每个参数设置值,被称为 JavaBean 模式。调用无参构造方法来创建对象,之后使用 setter 方法为参数赋值。setter 方法还可以返回对象本身,这样就可以链式调用了,类似下面这样:
1 2 3 4 5 6 7 UserDO userDO = new UserDO ();userDO.setUserCode(RandomStringUtils.randomNumeric(8 )) .setUserName("test user " + RandomStringUtils.randomAlphabetic(6 )) .setValidDate(LocalDate.now()) .setInvalidDate(LocalDate.now()) .setCompanyCode("" ) .setDepartmentCode(null );
这种方式也是有问题的。由于构造过程中调用的方法被分割成了多次调用,所以在构造过程中对象可能处于不一致的状态。该类没有通过检查构造参数的有效性来强制一致性的选项。在不一致的状态下尝试使用对象可能会导致一些错误,这些错误与平常代码的 BUG 很是不同,因此很难调试。一个相关的缺点是,这种方式排除了让类不可变的可能性,并且需要程序员增加工作以确保线程安全。
使用建造者模式,结合了可伸缩构造方法模式的安全性和 JavaBean 模式的可读性。客户端不直接构造所需的对象,而是调用一个包含所有必需参数的构造方法获得一个 builder 对象。然后,客户端调用 builder 对象的与 setter
相似方法来设置你想设置的可选参数。最后,客户端调用 builder 对象的一个无参的 build
方法来生成对象,该对象通常是不可变的。Builder 通常是它所构建的类的一个静态成员类。
使用建造者模式时,可以进行有效性检查 :
在 builder 的构造方法和方法中进行参数有效性检验。
在 build
方法调用的构造方法中检查包含多个参数的不变性。
在从 builder 向构建对象复制参数后对对象属性进行检查,校验失败时抛出 IllegalArgumentException
异常。
Builder 模式也有缺点。为了创建对象,首先必须创建它的 builder。虽然创建这个 builder 的成本在实践中不太可能被注意到,但在看中性能的场合下这可能就是一个问题。而且,builder 模式比伸缩构造方法模式更冗长,因此只有在有足够的参数时才值得使用它,比如四个或更多。有可能在以后会想要添加更多的参数,如果一开始使用的构造方法或静态工厂,当类演化到参数数量失控的时候再转到 Builder 模式,过时的构造方法或静态工厂就会面临尴尬的处境。因此,通常最好从一开始就创建一个 builder。
二、Lombok 中的 @Builder 注解 @Builder
注解为被注解的类或包含有成员被 @Builder
注解的类创建了一个所谓的“构建器”(builder)方面。
如果是一个成员被注解,则该成员必须是构造函数或方法。如果是一个类被注解,那么会生成一个包私有的构造函数,该构造函数以所有字段作为参数(就像在类上存在 @AllArgsConstructor(access = AccessLevel.PACKAGE)
一样),并且这个构造函数会被认为是用 @Builder
注解过的。需要注意的是,只有当你没有编写任何构造函数并且也没有添加任何显式的 @XArgsConstructor
注解时,才会生成这个构造函数。在这些情况下,Lombok 会假设一个全参构造函数已经存在,并生成使用它的代码;这意味着如果这个构造函数不存在,你会得到一个编译错误。
@Builder
的效果是生成一个名为 TBuilder
的内部类,它有一个私有的构造函数。TBuilder
实例是通过 builder()
方法创建的,该方法也会为你自动生成在类本身中(而不是在构建器类中)。
TBuilder
类为被注解的构造函数/方法的每个参数(当注解一个类时为每个字段)包含一个方法,这些方法返回构建器本身。构建器还包含一个 build()
方法,该方法返回一个完成的原始类型实例,它是通过将所有参数通过构建器中的各种其他方法设置后传递给被 @Builder
注解的构造函数或方法来创建的。此方法的返回类型将与相关类相同,除非是一个方法被注解,在这种情况下,它将等于该方法的返回类型。
简单来说,@Builder
注解的作用是:
自动为类生成一个构建器模式的实现。
如果注解应用于类,则自动提供一个带有所有字段作为参数的包私有构造函数。
提供一个静态的 builder()
方法来创建构建器实例。
在构建器类中为每个需要构建的属性生成相应的方法,这些方法返回构建器本身,以支持链式调用。
提供一个 build()
方法来最终构建所需的对象实例。
三、Spring 中的建造者模式示例 1、spring-web 中的 RequestEntity
以下为 spring 的 spring-web 中的 RequestEntity
,相对应的还有 ResponseEntity
,两者有共同父类 HttpEntity
。
1.1 RequestEntity
RequestEntity
的父类 HttpEntity
的代码(只展示属性):
1 2 3 4 5 6 7 8 9 public class HttpEntity <T> { public static final HttpEntity<?> EMPTY = new HttpEntity <>(); private final HttpHeaders headers; @Nullable private final T body; }
RequestEntity
类的属性:
1 2 3 4 5 6 7 8 9 10 11 public class RequestEntity <T> extends HttpEntity <T> { @Nullable private final HttpMethod method; @Nullable private final URI url; @Nullable private final Type type; }
RequestEntity
类的构造方法如下,可以看到构造方法使用了可伸缩构造方法 ,对于 RequestEntity
来说,method
和 url
属性是必须的。
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 public RequestEntity (HttpMethod method, URI url) { this (null , null , method, url, null ); } public RequestEntity (@Nullable T body, HttpMethod method, URI url) { this (body, null , method, url, null ); } public RequestEntity (@Nullable T body, HttpMethod method, URI url, Type type) { this (body, null , method, url, type); } public RequestEntity (MultiValueMap<String, String> headers, HttpMethod method, URI url) { this (null , headers, method, url, null ); } public RequestEntity (@Nullable T body, @Nullable MultiValueMap<String, String> headers, @Nullable HttpMethod method, URI url) { this (body, headers, method, url, null ); } public RequestEntity (@Nullable T body, @Nullable MultiValueMap<String, String> headers, @Nullable HttpMethod method, @Nullable URI url, @Nullable Type type) { super (body, headers); this .method = method; this .url = url; this .type = type; }
属性的 getter 方法
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 @Nullable public HttpMethod getMethod () { return this .method; } public URI getUrl () { if (this .url == null ) { throw new UnsupportedOperationException ( "The RequestEntity was created with a URI template and variables, " + "and there is not enough information on how to correctly expand and " + "encode the URI template. This will be done by the RestTemplate instead " + "with help from the UriTemplateHandler it is configured with." ); } return this .url; } @Nullable public Type getType () { if (this .type == null ) { T body = getBody(); if (body != null ) { return body.getClass(); } } return this .type; }
RequestEntity
中没有 setter 方法,只能通过构造方法或 builder 来创建对象或为属性赋值。
1.2 builder 接口 builder 接口定义了构造器的行为,HTTP 的结构分为 header 和 body,所以抽象出来两个接口,分别是 HeadersBuilder
和继承自 HeadersBuilder
的 BodyBuilder
,其中 HeadersBuilder
接口代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface HeadersBuilder <B extends HeadersBuilder <B>> { B header (String headerName, String... headerValues) ; B headers (@Nullable HttpHeaders headers) ; B headers (Consumer<HttpHeaders> headersConsumer) ; B accept (MediaType... acceptableMediaTypes) ; B acceptCharset (Charset... acceptableCharsets) ; B ifModifiedSince (ZonedDateTime ifModifiedSince) ; B ifModifiedSince (Instant ifModifiedSince) ; B ifModifiedSince (long ifModifiedSince) ; B ifNoneMatch (String... ifNoneMatches) ; RequestEntity<Void> build () ; }
这里的接口是一个带有递归类型参数(recursive type parameter)的泛型类型,允许方法链在子类中正常工作,而不需要强制转换。 Java 缺乏自我类型的这种变通解决方法被称为模拟自我类型(simulated self-type)。
HeadersBuilder
中的 build
方法会返回构造好的 RequestEntity
对象,构造只有 header 的 request 对象,没有 body,泛型类型使用 Void
。
BodyBuilder
接口如下:
1 2 3 4 5 6 7 8 9 10 public interface BodyBuilder extends HeadersBuilder <BodyBuilder> { BodyBuilder contentLength (long contentLength) ; BodyBuilder contentType (MediaType contentType) ; <T> RequestEntity<T> body (T body) ; <T> RequestEntity<T> body (T body, Type type) ; }
BodyBuilder
接口增加了两个获取构造对象 RequestEntity
的 body
方法。
1.3 builder 实现类 DefaultBodyBuilder
builder 实现类 DefaultBodyBuilder
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 private static class DefaultBodyBuilder implements BodyBuilder { private final HttpMethod method; private final HttpHeaders headers = new HttpHeaders (); @Nullable private final URI uri; @Nullable private final String uriTemplate; @Nullable private final Object[] uriVarsArray; @Nullable private final Map<String, ?> uriVarsMap; DefaultBodyBuilder(HttpMethod method, URI url) { this .method = method; this .uri = url; this .uriTemplate = null ; this .uriVarsArray = null ; this .uriVarsMap = null ; } DefaultBodyBuilder(HttpMethod method, String uriTemplate, Object... uriVars) { this .method = method; this .uri = null ; this .uriTemplate = uriTemplate; this .uriVarsArray = uriVars; this .uriVarsMap = null ; } DefaultBodyBuilder(HttpMethod method, String uriTemplate, Map<String, ?> uriVars) { this .method = method; this .uri = null ; this .uriTemplate = uriTemplate; this .uriVarsArray = null ; this .uriVarsMap = uriVars; } @Override public BodyBuilder header (String headerName, String... headerValues) { for (String headerValue : headerValues) { this .headers.add(headerName, headerValue); } return this ; } @Override public BodyBuilder headers (@Nullable HttpHeaders headers) { if (headers != null ) { this .headers.putAll(headers); } return this ; } @Override public BodyBuilder headers (Consumer<HttpHeaders> headersConsumer) { headersConsumer.accept(this .headers); return this ; } @Override public BodyBuilder accept (MediaType... acceptableMediaTypes) { this .headers.setAccept(Arrays.asList(acceptableMediaTypes)); return this ; } @Override public BodyBuilder acceptCharset (Charset... acceptableCharsets) { this .headers.setAcceptCharset(Arrays.asList(acceptableCharsets)); return this ; } @Override public BodyBuilder contentLength (long contentLength) { this .headers.setContentLength(contentLength); return this ; } @Override public BodyBuilder contentType (MediaType contentType) { this .headers.setContentType(contentType); return this ; } @Override public BodyBuilder ifModifiedSince (ZonedDateTime ifModifiedSince) { this .headers.setIfModifiedSince(ifModifiedSince); return this ; } @Override public BodyBuilder ifModifiedSince (Instant ifModifiedSince) { this .headers.setIfModifiedSince(ifModifiedSince); return this ; } @Override public BodyBuilder ifModifiedSince (long ifModifiedSince) { this .headers.setIfModifiedSince(ifModifiedSince); return this ; } @Override public BodyBuilder ifNoneMatch (String... ifNoneMatches) { this .headers.setIfNoneMatch(Arrays.asList(ifNoneMatches)); return this ; } @Override public RequestEntity<Void> build () { return buildInternal(null , null ); } @Override public <T> RequestEntity<T> body (T body) { return buildInternal(body, null ); } @Override public <T> RequestEntity<T> body (T body, Type type) { return buildInternal(body, type); } private <T> RequestEntity<T> buildInternal (@Nullable T body, @Nullable Type type) { if (this .uri != null ) { return new RequestEntity <>(body, this .headers, this .method, this .uri, type); } else if (this .uriTemplate != null ){ return new UriTemplateRequestEntity <>(body, this .headers, this .method, type, this .uriTemplate, this .uriVarsArray, this .uriVarsMap); } else { throw new IllegalStateException ("Neither URI nor URI template" ); } } }
DefaultBodyBuilder
为私有静态内部类,实现了 HeadersBuilder
和 BodyBuilder
接口的方法。
1.4 RequestEntity
中获取 builder 的方法 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public static BodyBuilder method (HttpMethod method, URI url) { return new DefaultBodyBuilder (method, url); } public static BodyBuilder method (HttpMethod method, String uriTemplate, Object... uriVariables) { return new DefaultBodyBuilder (method, uriTemplate, uriVariables); } public static BodyBuilder method (HttpMethod method, String uriTemplate, Map<String, ?> uriVariables) { return new DefaultBodyBuilder (method, uriTemplate, uriVariables); } public static HeadersBuilder<?> get(URI url) { return method(HttpMethod.GET, url); } public static HeadersBuilder<?> get(String uriTemplate, Object... uriVariables) { return method(HttpMethod.GET, uriTemplate, uriVariables); } public static HeadersBuilder<?> head(URI url) { return method(HttpMethod.HEAD, url); } public static HeadersBuilder<?> head(String uriTemplate, Object... uriVariables) { return method(HttpMethod.HEAD, uriTemplate, uriVariables); } public static BodyBuilder post (URI url) { return method(HttpMethod.POST, url); } public static BodyBuilder post (String uriTemplate, Object... uriVariables) { return method(HttpMethod.POST, uriTemplate, uriVariables); } public static BodyBuilder put (URI url) { return method(HttpMethod.PUT, url); } public static BodyBuilder put (String uriTemplate, Object... uriVariables) { return method(HttpMethod.PUT, uriTemplate, uriVariables); } public static BodyBuilder patch (URI url) { return method(HttpMethod.PATCH, url); } public static BodyBuilder patch (String uriTemplate, Object... uriVariables) { return method(HttpMethod.PATCH, uriTemplate, uriVariables); } public static HeadersBuilder<?> delete(URI url) { return method(HttpMethod.DELETE, url); } public static HeadersBuilder<?> delete(String uriTemplate, Object... uriVariables) { return method(HttpMethod.DELETE, uriTemplate, uriVariables); } public static HeadersBuilder<?> options(URI url) { return method(HttpMethod.OPTIONS, url); } public static HeadersBuilder<?> options(String uriTemplate, Object... uriVariables) { return method(HttpMethod.OPTIONS, uriTemplate, uriVariables); }
相关链接 模拟自我类型 | z2huo
OB links [[模拟自我类型]]
#设计模式