场景 (1)构造一个复杂的对象,很多的属性,有些属性构造的时候需要做一些校验,格式转换

1.常规

  1. package com.example.demo.pattern.builder;
  2. /**
  3. * @author chenchao
  4. * @date 2021/11/9
  5. */
  6. public class WithoutBuilderPatternDemo {
  7. public static void main(String[] args) {
  8. // 构造这个复杂的product对象
  9. Product product = new Product();
  10. // 设置field1属性
  11. System.out.println("在设置field1之前进行复杂的校验逻辑");
  12. product.setField1("值1");
  13. // 设置field2属性
  14. System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
  15. product.setField2("值2");
  16. // 设置field3属性
  17. System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
  18. product.setField3("值3");
  19. // 上面是简化的一个逻辑,实际上对于一些有几十个字段,甚至是上百个字段的复杂对象的构建
  20. // 上面那段代码会极度膨胀,非常复杂
  21. // 一个是说,大量代码堆积在一起,维护性非常差,可读性非常差,一坨代码,跟屎一样,读不懂,没法改
  22. // 另外一个,就是说,这段逻辑,如果在多个地方都有使用的话,一旦这段逻辑出现了一些变化,那么可能就需要
  23. // 在多个地方修改这一大坨跟屎一样的代码
  24. // 把不同的构造的步骤,抽取成某一个方法
  25. }
  26. public static class Product {
  27. private String field1;
  28. private String field2;
  29. private String field3;
  30. public String getField1() {
  31. return field1;
  32. }
  33. public void setField1(String field1) {
  34. this.field1 = field1;
  35. }
  36. public String getField2() {
  37. return field2;
  38. }
  39. public void setField2(String field2) {
  40. this.field2 = field2;
  41. }
  42. public String getField3() {
  43. return field3;
  44. }
  45. public void setField3(String field3) {
  46. this.field3 = field3;
  47. }
  48. }
  49. }

2.构造器模式

  1. package com.example.demo.pattern.builder;
  2. /**
  3. * @author chenchao
  4. * @date 2021/11/9
  5. */
  6. public class BuilderPatternDemo {
  7. public static void main(String[] args) {
  8. Director director = new Director(new ConcreteBuilder());
  9. Product product = director.build("值1", "值2", "值3");
  10. System.out.println(product);
  11. // 好处1:通过builder接口将复杂构建步骤拆分成了多个部分,代码逻辑清晰,维护性和扩展性都很好
  12. // 好处2:将对象构建的过程,封装在了director里面,director来基于builder进行构建,构建逻辑修改,不需要修改很多地方
  13. // 好处3:相对于工厂,有一个很好的抽象设计,director和builder
  14. }
  15. public static class Product {
  16. private String field1;
  17. private String field2;
  18. private String field3;
  19. public String getField1() {
  20. return field1;
  21. }
  22. public void setField1(String field1) {
  23. this.field1 = field1;
  24. }
  25. public String getField2() {
  26. return field2;
  27. }
  28. public void setField2(String field2) {
  29. this.field2 = field2;
  30. }
  31. public String getField3() {
  32. return field3;
  33. }
  34. public void setField3(String field3) {
  35. this.field3 = field3;
  36. }
  37. @Override
  38. public String toString() {
  39. return "Product [field1=" + field1 + ", field2=" + field2 + ", field3=" + field3 + "]";
  40. }
  41. }
  42. public interface Builder {
  43. void field1(String value);
  44. void field2(String value);
  45. void field3(String value);
  46. Product create();
  47. }
  48. public static class ConcreteBuilder implements Builder {
  49. private Product product = new Product();
  50. @Override
  51. public void field1(String value) {
  52. System.out.println("在设置field1之前进行复杂的校验逻辑");
  53. product.setField1(value);
  54. }
  55. @Override
  56. public void field2(String value) {
  57. System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
  58. product.setField2(value);
  59. }
  60. @Override
  61. public void field3(String value) {
  62. System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
  63. product.setField3(value);
  64. }
  65. @Override
  66. public Product create() {
  67. return product;
  68. }
  69. }
  70. /**
  71. * director是面向builder的接口,来编程的
  72. * director可以复杂控制构建的一个步骤,具体的每个步骤的逻辑封装在具体的builder类中
  73. * 如果我们此时要更换一整套的构建逻辑,可以再搞一个新的builder类就可以了
  74. * 但是我们的整个构建步骤是没有任何改变的
  75. *
  76. * 如果整个构建步骤变化了,但是对构建的逻辑是没有影响的
  77. *
  78. * @author chenchao
  79. *
  80. */
  81. public static class Director {
  82. private Builder builder;
  83. public Director(Builder builder) {
  84. this.builder = builder;
  85. }
  86. public Product build(String field1, String field2, String field3) {
  87. builder.field1(field1);
  88. builder.field2(field2);
  89. builder.field3(field3);
  90. return builder.create();
  91. }
  92. }
  93. }

3.升级 搭配工厂模式

  1. package com.example.demo.pattern.builder;
  2. /**
  3. * @author chenchao
  4. * @date 2021/11/9
  5. */
  6. public class OptimizedBuilderPatternDemo {
  7. public static void main(String[] args) {
  8. Product product = new ConcreteBuilder()
  9. .field1("值1")
  10. .field2("值2")
  11. .field3("值3")
  12. .create();
  13. System.out.println(product);
  14. // 现在基本上流行的一些开源框架,构造器模式的运用,一般都是上面这种变种模式
  15. }
  16. public static class Product {
  17. private String field1;
  18. private String field2;
  19. private String field3;
  20. public String getField1() {
  21. return field1;
  22. }
  23. public void setField1(String field1) {
  24. this.field1 = field1;
  25. }
  26. public String getField2() {
  27. return field2;
  28. }
  29. public void setField2(String field2) {
  30. this.field2 = field2;
  31. }
  32. public String getField3() {
  33. return field3;
  34. }
  35. public void setField3(String field3) {
  36. this.field3 = field3;
  37. }
  38. @Override
  39. public String toString() {
  40. return "Product [field1=" + field1 + ", field2=" + field2 + ", field3=" + field3 + "]";
  41. }
  42. }
  43. public interface Builder {
  44. Builder field1(String value);
  45. Builder field2(String value);
  46. Builder field3(String value);
  47. Product create();
  48. }
  49. public static class ConcreteBuilder implements Builder {
  50. private Product product = new Product();
  51. public Builder field1(String value) {
  52. System.out.println("在设置field1之前进行复杂的校验逻辑");
  53. product.setField1(value);
  54. return this;
  55. }
  56. public Builder field2(String value) {
  57. System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
  58. product.setField2(value);
  59. return this;
  60. }
  61. public Builder field3(String value) {
  62. System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
  63. product.setField3(value);
  64. return this;
  65. }
  66. public Product create() {
  67. return product;
  68. }
  69. }
  70. }

4.说明

构造器是一种非常棒,非常实用,非常常用的设计模式

常见于在构建一个复杂的对象,或者是构建一个复杂的表达式的时候,在开源框架中有大量广泛的运用

给大家举个例子,Mockito框架中,when().thenReturn()之类的,其实就是构造器模式的一种,通过多个连续的方法完成一个模拟对象的构建;还有就是spring test框架汇总,MvcMock对象,也是通过thenExpected()等多个连续的方法,完成一个mvc测试对象的构建。

用构造器模式的最大好处是啥?复杂对象的构建过程太复杂了,里面可能包含一些业务逻辑,比如值检查,格式转换之类的。如果每个客户端都自己手动去完成构建的话,那么大量的冗余代码是一个;另外一个,如果我们要改变对象的构建过程的实现,可以就在构造器中一个地方修改即可,对于调用方完全透明;最后,如果我们要完全替换掉以前的builder实现的话,那么完全可以在工厂里替换一个实现,还跟简单工厂模式结合起来使用了