设计模式(三)建造者模式
引出问题:
很多人应该遇到这么一种情况,我需要一个对象,但是这个对象比较复杂,有很多属性,你希望在创建对象的同时初始化这些属性。
很自然的,你会想到为这些属性创建对应参数的构造器。
那么问题又来了,如果这些属性,有些时候有,有些时候没有,怎么办呢?
数据模型:
1 @EqualsAndHashCode 2 @Data 3 public class PolicyCommon { 4 5 private String code; 6 private String category; 7 private String name; 8 private String inceptionDate; 9 10 public PolicyCommon() { 11 super(); 12 } 13 14 public PolicyCommon(String code, String category, String name) { 15 super(); 16 this.code = code; 17 this.category = category; 18 this.name = name; 19 } 20 21 }
分析:
以上这个模型,一共拥有4个属性,假定 inceptionDate 属性,在创建对象的时候不需要,其他三个属性都可有可无。
如此一来,初始化的对象可能出现的状态个数为:2^3=8
传统意义上的解决方案:
通过构造器的设计,可以解决这个问题,有两种方案:
- 提供无参构造器,优先创建一个空对象,然后依次调用 set 方法。
- 提供三个参数的构造器,如果某个属性不需要,构造器对应的参数上赋值 null。
以上两种设计,都有各自的问题:
第一种,问题在于,讲构造对象的动作,拆分成多次,导致对象不是一次性构造完成,在中间步骤中出现对象不一致的情况。
第二种,问题在于,从代码上看,属性的意义不明显,如果这是一个不需要任何属性的,如:new PolicyCommon(null, "", null),使用者怎么知道第二个参数到底是什么?
使用建造者模式:
在 《Effective Java》 中,强烈建议在遇到这种复杂的情况时,使用建造者模式来完成创建对象的任务。
建造者模式(Builder Pattern),也称生成器模式,定义如下:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Separate the construction of a complex object from its representation so that the same construction process can create different representation.
思想:
使用一个静态内部类 Builder,Builder 中持有外部类中,需要动态生成的属性。
Builder 为每一个属性,提供 set() 方法,但是与通常的set() 方法不同,这些 set() 方法的返回值是 Builder 本身。
Builder 提供一个 build() 方法,返回类型为外部类。
外部类中,仅仅提供一个参数为 Builder 对象的构造器,且将其的权限设置为 private,如此一来,构造器的唯一途径就是 build() 方法。
代码:
1 public final class BuilderTest { 2 3 @Test 4 public void test() { 5 PolicyCommon policy1 = new PolicyCommon(); 6 policy1.setName(""); 7 policy1.setCode(""); 8 PolicyCommon policy2 = new PolicyCommon("", null, ""); 9 Assert.assertEquals(policy1, policy2); 10 PolicyBuilder policy3 = new PolicyBuilder.Builder().setName("").setCode("").build(); 11 PolicyBuilderLombok policy4 = new PolicyBuilderLombok.PolicyBuilderLombokBuilder().name("").code("").build(); 12 Assert.assertEquals(policy3.getName(), policy4.getName()); 13 Assert.assertEquals(policy3.getCode(), policy4.getCode()); 14 Assert.assertEquals(policy3.getCategory(), policy4.getCategory()); 15 Assert.assertEquals(policy3.getInceptionDate(), policy4.getInceptionDate()); 16 } 17 18 }JUnit
可以看出,除了名称之外,@Builder 注解调用的方式与自己实现建造者模式,并没有什么不同,如果没有一些特殊的要求,Lombok 的 @Builder 注解,可以成为主流的建造者模式的使用方式。
- 设计模式(六): 建造者模式
- 设计模式(6)之建造者模式
- 设计模式C++实现(6)——建造者模式
- 设计模式Builder(建造者)模式
- 设计模式Before-after之建造者模式
- 设计模式笔记四:建造者模式
- 设计模式C++实现(6)——建造者模式
- 设计模式(三) 建造者模式
- 《设计模式》之建造者模式
- 设计模式(五)——建造者模式实例
- 设计模式C++实现(6)——建造者模式
- 设计模式Builder(建造者)模式
- 设计模式总结笔记<三> 建造者模式前篇
- 设计模式系列(四)建造者模式Builder
- 设计模式C++实现(6)——建造者模式
- 设计模式(九):建造者模式
- 设计模式总结笔记<三> 建造者模式后篇
- 设计模式的艺术之道--建造者模式
- 设计模式(十六):建造者模式
- 设计模式(9)--建造者模式