设计模式(四)建造者模式 Builder
- 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);
}
这样做的劣势在于:
- 从调用者的角度来说,属性的意义不明显,很容易将参数位置颠倒了,但是编译器并没有报错,例如: new PolicyCommon("Engineer", "Gerrard", "11123768");
- 如果我只想初始化一个属性,如 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的更多相关文章
- 乐在其中设计模式(C#) - 建造者模式(Builder Pattern)
原文:乐在其中设计模式(C#) - 建造者模式(Builder Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 建造者模式(Builder Pattern) 作者:webabc ...
- 二十四种设计模式:建造者模式(Builder Pattern)
建造者模式(Builder Pattern) 介绍将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 示例用同样的构建过程创建Sql和Xml的Insert()方法和Get()方 ...
- 【设计模式】建造者模式 Builder Pattern
前面学习了简单工厂模式,工厂方法模式以及抽象工厂模式,这些都是创建类的对象所使用的一些常用的方法和套路, 那么如果我们创建一个很复杂的对象可上面的三种方法都不太适合,那么“专业的事交给专业人去做”,2 ...
- 设计模式之建造者模式——Builder
一.概述 Builder模式,中文名为建造者模式,又名生成器模式.构建者模式等,是创建型设计模式之一.用于将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 1.适用性: 对象 ...
- Python 设计模式之建造者模式 Builder Pattern
#引入建造者模式 肯德基的菜单上有 薯条, 鸡腿,鸡翅,鸡米花,可乐,橙汁,火腿汉堡,至尊虾汉堡,牛肉汉堡 , 鸡肉卷等这些单品,也有很多套餐. 比如 套餐1:鸡翅,至尊虾汉堡,可乐,薯条 套餐2:鸡 ...
- 设计模式-05建造者模式(Builder Pattern)
1.模式动机 比如我们要组装一台电脑,都知道电脑是由 CPU.主板.内存.硬盘.显卡.机箱.显示器.键盘和鼠标组成,其中非常重要的一点就是这些硬件都是可以灵活选择,但是组装步骤都是大同小异(可以组一个 ...
- 设计模式之建造者模式(Builder)
建造者模式原理:建造模式主要是用于产生对象的各个组成部分,而抽象工厂模式则用于产生一系列对象,建造者模式而且要求这些对象的组成部分有序. 代码如下: #include <iostream> ...
- 设计模式之建造者模式Builder(创建型)
1. 概述 在软件开发的过程中,当遇到一个“复杂的对象”的创建工作,该对象由一定各个部分的子对象用一定的算法构成,由于需求的变化,复杂对象的各个部分经常面临剧烈的变化,但将它们组合在一起的算法相对稳定 ...
- 【UE4 设计模式】建造者模式 Builder Pattern
概述 描述 建造者模式,又称生成器模式.是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 建造者模式将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无需知道复杂 ...
- 创建型设计模式之建造者模式(Builder)
结构 意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 适用性 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时. 当构造过程必须允许被构造的对象有不 ...
随机推荐
- 为什么我的C4C Service Request没办法Release到ERP?
问题 UI上发现找不到Release to ERP的按钮: 但是在UI Designer里是能看到这个按钮的.检查其Visible的属性,绑到了一个Calculated Rule上面: 发现其显示在r ...
- 企业数字化转型与SAP云平台
我们生活在一个数字化时代.信息领域里发展迅猛的数字技术和成本不断降低的硬件设备,正以前所未有的方式改变着我们工作和生活的方式. Digital Mesh 美国一家著名的从事信息技术研究和提供咨询服务的 ...
- UVALive 3523 Knights of the Round Table 圆桌骑士 (无向图点双连通分量)
由于互相憎恨的骑士不能相邻,把可以相邻的骑士连上无向边,会议要求是奇数,问题就是求不在任意一个简单奇圈上的结点个数. 如果不是二分图,一定存在一个奇圈,同一个双连通分量中其它点一定可以加入奇圈.很明显 ...
- Android(java)学习笔记107:Relativelayout相对布局
1. Relativelayout相对布局案例: 我们看看案例代码,自己心领神会: <?xml version="1.0" encoding="utf-8" ...
- Android开发出现 StackOverflowError
问题:StackOverflowError 在HTC或者摩托罗拉的手机上测试出现 StackOverflowError 的错误. 06-12 10:28:31.750: E/AndroidRuntim ...
- python_105_类的特殊成员方法
aa.py class C(): def __init__(self): self.name='QiZhiguang' 类的特殊成员方法: # 1. __doc__ 表示类的描述信息 class Do ...
- 【NOIP提高A组模拟2018.8.14】 区间
区间加:差分数组修改 O(n)扫描,负数位置单调不减 #include<iostream> #include<cstring> #include<cstdio> # ...
- Tarjan算法 详解+心得
Tarjan算法是由Robert Tarjan(罗伯特·塔扬,不知有几位大神读对过这个名字) 发明的求有向图中强连通分量的算法. 预备知识:有向图,强连通. 有向图:由有向边的构成的图.需要注意的是这 ...
- Golang 谷歌搜索api 实现搜索引擎(前端 bootstrap + jquery)
Golang 谷歌搜索api 实现搜索引擎(前端 bootstrap + jquery) 体验 冒号搜索 1. 获取谷歌搜索api 谷歌搜索api教程 2. 后台调用 程序入口 main.go // ...
- python爬虫: 豆瓣电影top250数据分析
转载博客 https://segmentfault.com/a/1190000005920679 根据自己的环境修改并配置mysql数据库 系统:Mac OS X 10.11 python 2.7 m ...