• Builder:

  《Effective Java》 第2条:遇到多个构造器参数时要考虑用构建器。

  建造者模式(Builder Pattern),也称生成器模式,定义如下:

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

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

  • 什么时候使用 Builder:

  我们常常会看见这种情况:我需要一个初始化一个复杂对象,在初始化的同时完成参数的赋值工作。

  • 数据模型:
@Data
@EqualsAndHashCode
@ToString
public class PolicyCommon { private String code;
private String category;
private String name;
private String inceptionDate; public PolicyCommon() {
super();
} public PolicyCommon(String code, String category, String name) {
super();
this.code = code;
this.category = category;
this.name = name;
}
}
  • 模型分析:

  PolicyCommon 带有4个参数,假定只有 inceptionDate不需要在创建的时候赋值。

  剩余3个参数参与初始化的工作,那么这个对象初始化之后的状态可能性为:2^3=8。

  • 传统的解决方案1——使用无参构造器,然后依次调用 set() 方法:
    @Test
void testPolicyCommon1() {
PolicyCommon policy = new PolicyCommon();
policy.setName("Gerrard");
policy.setCode("11123768");
policy.setCategory("Engineer");
System.out.println(policy);
}

  这种方法,将构造对象的过程拆分成多个动作,是存在风险的。

  因为对象不是一次性构造完成,使得对象在构造过程中存在状态不一致的情况。

  期间有 this 指针溢出的风险,阻碍了这个对象称为不可变对象的可能。在多线程条件下,需要额外的工作才能保证线程安全。

  • 传统的解决方案2——使用重叠参数的构造器:
    @Test
void testPolicyCommon2() {
PolicyCommon policy = new PolicyCommon("11123768", "Engineer", "Gerrard");
System.out.println(policy);
}

  这样做的劣势在于:

  1. 从调用者的角度来说,属性的意义不明显,很容易将参数位置颠倒了,但是编译器并没有报错,例如: new PolicyCommon("Engineer", "Gerrard", "11123768");
  2. 如果我只想初始化一个属性,如 code,那么构造器的调用会有许多冗余参数。例如:new PolicyCommon("11123768", null, null);
  • Builder 给出的解决方案:

  使用一个静态内部类 Builder,Builder 持有需要动态生成的属性。

  Builder 为每一个属性,提供 set() 方法,但是与通常的set() 方法不同,这些 set() 方法的返回值是 Builder 本身。

  Builder 提供一个 build() 方法,返回类型为外部类。

  外部类中,提供一个参数为 Builder 对象的构造器,且将其的权限设置为 private,如此一来,构造器的唯一途径就是 build() 方法。

  • 代码:
@Data
@EqualsAndHashCode
@ToString
public class PolicyBuilder { private String code;
private String category;
private String name;
private String inceptionDate; private PolicyBuilder(Builder builder) {
code = builder.code;
category = builder.category;
name = builder.name;
} public static class Builder { private String code;
private String category;
private String name; public Builder setCode(String code) {
this.code = code;
return this;
} public Builder setCategory(String category) {
this.category = category;
return this;
} public Builder setName(String name) {
this.name = name;
return this;
} public PolicyBuilder build() {
return new PolicyBuilder(this);
}
}
}
  • 调用 Builder:
    @Test
void testPolicyBuilder() {
PolicyBuilder policy = new PolicyBuilder.Builder().setName("Gerrard").setCode("11123768").build();
System.out.println(policy);
}

  可以看出,Builder 初始化的过程中,对参数选择更加自由,而且它不会使对象处于不同的状态。

  劣势在于冗长的内部类代码,以及构建过程中会增加一个 Builder 对象。

  • 使用 Lombok 一键 Builder:

  细心的朋友,在最初的 PolicyCommon 的数据模型中,会发现三个注解,这是 Lombok 框架的功能(org.projectlombok,License = MIT)。

  @Data,为类的每一个属性,自动生成 get() 和 set() 方法。

  @EqualsAndHashCode,为类生成 equals() 和 hashCode() 方法。

  @ToString,为类生成 toString() 方法。

  除此之外,Lombok 还提供了 @Builder 的注解,自动为类提供了 Builder 的功能。

  • Lombok Builder:
@Data
@EqualsAndHashCode
@ToString
public class PolicyBuilderLombok { private String code;
private String category;
private String name;
private String inceptionDate; @Builder
public PolicyBuilderLombok(String code, String category, String name) {
super();
this.code = code;
this.category = category;
this.name = name;
}
}
    @Test
void testPolicyBuilderLombok() {
PolicyBuilderLombok policy = new PolicyBuilderLombok.PolicyBuilderLombokBuilder()
.name("Gerrard").code("11123768").build();
System.out.println(policy);
}
  • 解析 @Builder:

  检查元注解:@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})

  这表明这个注解可以加在:类、构造器、方法(不推荐加在方法上)。

  @Builder 加在类上 <==> ,在类的全参构造器上加上 @Builder。

  @Builder 加在构造器上,这个构造器的入参就是内部类 Builder 的动态参数。

设计模式(四)建造者模式 Builder的更多相关文章

  1. 乐在其中设计模式(C#) - 建造者模式(Builder Pattern)

    原文:乐在其中设计模式(C#) - 建造者模式(Builder Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 建造者模式(Builder Pattern) 作者:webabc ...

  2. 二十四种设计模式:建造者模式(Builder Pattern)

    建造者模式(Builder Pattern) 介绍将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 示例用同样的构建过程创建Sql和Xml的Insert()方法和Get()方 ...

  3. 【设计模式】建造者模式 Builder Pattern

    前面学习了简单工厂模式,工厂方法模式以及抽象工厂模式,这些都是创建类的对象所使用的一些常用的方法和套路, 那么如果我们创建一个很复杂的对象可上面的三种方法都不太适合,那么“专业的事交给专业人去做”,2 ...

  4. 设计模式之建造者模式——Builder

    一.概述 Builder模式,中文名为建造者模式,又名生成器模式.构建者模式等,是创建型设计模式之一.用于将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 1.适用性: 对象 ...

  5. Python 设计模式之建造者模式 Builder Pattern

    #引入建造者模式 肯德基的菜单上有 薯条, 鸡腿,鸡翅,鸡米花,可乐,橙汁,火腿汉堡,至尊虾汉堡,牛肉汉堡 , 鸡肉卷等这些单品,也有很多套餐. 比如 套餐1:鸡翅,至尊虾汉堡,可乐,薯条 套餐2:鸡 ...

  6. 设计模式-05建造者模式(Builder Pattern)

    1.模式动机 比如我们要组装一台电脑,都知道电脑是由 CPU.主板.内存.硬盘.显卡.机箱.显示器.键盘和鼠标组成,其中非常重要的一点就是这些硬件都是可以灵活选择,但是组装步骤都是大同小异(可以组一个 ...

  7. 设计模式之建造者模式(Builder)

    建造者模式原理:建造模式主要是用于产生对象的各个组成部分,而抽象工厂模式则用于产生一系列对象,建造者模式而且要求这些对象的组成部分有序. 代码如下: #include <iostream> ...

  8. 设计模式之建造者模式Builder(创建型)

    1. 概述 在软件开发的过程中,当遇到一个“复杂的对象”的创建工作,该对象由一定各个部分的子对象用一定的算法构成,由于需求的变化,复杂对象的各个部分经常面临剧烈的变化,但将它们组合在一起的算法相对稳定 ...

  9. 【UE4 设计模式】建造者模式 Builder Pattern

    概述 描述 建造者模式,又称生成器模式.是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 建造者模式将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无需知道复杂 ...

  10. 创建型设计模式之建造者模式(Builder)

    结构 意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 适用性 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时. 当构造过程必须允许被构造的对象有不 ...

随机推荐

  1. UVA 1611 Crane 起重机 (子问题)

    题意:给一个1~n排列,1<=n<=10000,每次操作选取一个长度为偶数的连续区间.交换前一半和后一半,使它变成升序. 题解:每次只要把最小的移动到最左边,那么问题规模就缩小了.假设当前 ...

  2. UVA 1451 Average平均值 (数形结合,斜率优化)

    摘要:数形结合,斜率优化,单调队列. 题意:求一个长度为n的01串的子串,子串长度至少为L,平均值应该尽量大,多个满足条件取长度最短,还有多个的话,取起点最靠左. 求出前缀和S[i],令点Pi表示(i ...

  3. codeforce Gym 100500E IBM Chill Zone (SG函数)

    关于sg函数这篇blog讲得很详细http://blog.csdn.net/logic_nut/article/details/4711489. sg函数的价值在于把复杂的游戏拆分成简单的游戏,然后通 ...

  4. js各运算符的执行顺序

    本文原链接:https://www.sojson.com/operation/javascript.html https://www.jianshu.com/p/d569c6ca1060 JavaSc ...

  5. JavaScript 获取对象中第一个属性

    使用 Object.keys(object) 可以取出属性名为数组,但会打乱顺序 严格意义上对象中是只有映射关系而没有顺序的,但是在存储结构里是有顺序的,如果想获取存储结构里的第一个属性可以使用for ...

  6. js 去除数组中的空值以及数组判断是否有重复数据

    1.判断是否有重复数据 function isRepeat(array){ var hash = {}; for(var i in array) { if(array[i]!="" ...

  7. Java执行系统命令工具类(JDK自带功能)

    CommandUtil.java package utils; import java.io.ByteArrayOutputStream; import java.io.IOException; im ...

  8. java 自定义容器,实现foreach

    import java.util.Arrays; import java.util.Iterator; public class ArrayList implements Iterable<In ...

  9. c++链表-双向链表+增删查改

    基于双向链表的增删改查和排序(C++实现) 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前 ...

  10. ARC中__weak;__strong;__unsafe_unretained;修饰词

    测试代码: // Human.h代码 @interface Human : NSObject @property (nonatomic, weak) Cat *pinkCat; @property ( ...