• 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. TCP连接建立与关闭

    http://hi.baidu.com/psorqkxcsfbbghd/item/70f3bd91943b9248f14215cd TCP连接建立与关闭 TCP 是一个面向连接的协议,无论哪一方向另一 ...

  2. 为什么我的C4C Service Request没办法Release到ERP?

    问题 UI上发现找不到Release to ERP的按钮: 但是在UI Designer里是能看到这个按钮的.检查其Visible的属性,绑到了一个Calculated Rule上面: 发现其显示在r ...

  3. 弄了一个星期的wp 8.1,吐血的感觉

    看到8.1出来这么久了,心痒难耐,忍不住想重新把应用写一遍,于是上个星期开始动手,用的mvvm模式,结果一路下来,sqlce不能用了,那好吧,我用sqlite,webrequest变成httpclie ...

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

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

  5. XPath语法规则及实例

    XPath语法规则及实例 XPath语法规则 一.XPath术语: 1.节点:在XPath中,有七种类型的节点:元素.属性.文本.命名空间.处理指令.注释以及文档(根)节点. XML文档是被作为节点树 ...

  6. Java写诗程序

    import java.util.Random; public class test_word { public static void main(String[] args) { System.ou ...

  7. mysql group by的特殊性

    SELECT create_year, userno , sum(sal) FROM user GROUP BY userno 以上语句,在oracle 或sql server肯定是语法错误  因为g ...

  8. C# 调用腾讯地图WebService API获取距离(一对多)

    官方文档地址:https://lbs.qq.com/webservice_v1/guide-distance.html 代码: /// <summary> /// 获取距离最近的点的经纬度 ...

  9. Golang Json测试

    结构体是谷歌搜索API package main import ( "encoding/json" "fmt" "io/ioutil" &q ...

  10. visual studio 2013 for windows desk报error MSB8020: The build tools for v141错误

    由于硬件限制,学习在touchgfx暂时在Windows下模拟仿真,了解其基本原理和有一个基本感性认识,因此安装了VS Express 2013 for Desktop轻量级编译器. 有TouchGF ...