概述

建造者模式(Builder Pattern)是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式属于创建型模式,对使用者而言,只需要指定需要建造的类型就可以获得对象,建造过程和细节不需要了解。
建造者模式的概念听上去有点抽象,但是实际上可以这么说,基本上只要做 Web 开发的基本都用过,只是可能自己不知道这就是建造者模式而已。

示例

我们以家庭作业为例,假设老师会根据每个不同基础的同学布置不同难度的题目来实现一个简单的建造者模式(这里我们需要新建一个 builder 目录,相关类创建在 builder 目录下)。

  • 首先建造一个家庭作业类 Homework.java,这个类里面有四个属性,分别代表四种不同难度的家庭作业。
  1. package builder;
  2. public class Homework {
  3. private String easyQc;//简答题目
  4. private String normalQc;//正常题目
  5. private String MediumQc;//中等难度题目
  6. private String HardQc;//困难题目
  7. public String getEasyQc() {
  8. return easyQc;
  9. }
  10. public void setEasyQc(String easyQc) {
  11. this.easyQc = easyQc;
  12. }
  13. public String getNormalQc() {
  14. return normalQc;
  15. }
  16. public void setNormalQc(String normalQc) {
  17. this.normalQc = normalQc;
  18. }
  19. public String getMediumQc() {
  20. return MediumQc;
  21. }
  22. public void setMediumQc(String mediumQc) {
  23. MediumQc = mediumQc;
  24. }
  25. public String getHardQc() {
  26. return HardQc;
  27. }
  28. public void setHardQc(String hardQc) {
  29. HardQc = hardQc;
  30. }
  31. }
  • 然后新建一个建造者类 SimpleBuilder.java,建造者类就是用来创建对象的。 ```java package builder;

public class SimpleBuilder { private Homework homework;

  1. public SimpleBuilder(Homework homework) {
  2. this.homework = homework;
  3. }
  4. public void buildEasyQc(String easyQc){
  5. homework.setEasyQc(easyQc);
  6. }
  7. public void buildNormalQc(String normalQc){
  8. homework.setNormalQc(normalQc);
  9. }
  10. public void buildMediumQc(String mediumQc){
  11. homework.setMediumQc(mediumQc);
  12. }
  13. public void buildHardQc(String hardQc){
  14. homework.setHardQc(hardQc);
  15. }
  16. public Homework build(){
  17. return homework;
  18. }

}

  1. 这个建造者类里面持有了家庭作业对象,并且为其每个属性都提供了一个方法进行赋值。
  2. - 最后我们新建一个测试类 TestSimpleBuilder.java 来测试一下。
  3. ```java
  4. package builder;
  5. public class TestSimpleBuilder {
  6. public static void main(String[] args) {
  7. SimpleBuilder simpleBuilder = new SimpleBuilder(new Homework());
  8. simpleBuilder.buildEasyQc("简单题目");//1
  9. simpleBuilder.buildNormalQc("标准难度题目");//2
  10. simpleBuilder.buildMediumQc("中等难度题目");//3
  11. simpleBuilder.buildHardQc("高难度题目");//4
  12. Homework homework = simpleBuilder.build();
  13. StringBuilder sb = new StringBuilder();
  14. sb.append(null == homework.getEasyQc() ? "" : homework.getEasyQc() + ",");
  15. sb.append(null == homework.getNormalQc() ? "" : homework.getNormalQc() + ",");
  16. sb.append(null == homework.getMediumQc() ? "" : homework.getMediumQc() + ",");
  17. sb.append(null == homework.getHardQc() ? "" : homework.getHardQc());
  18. System.out.println("我的家庭作业有:" + sb.toString());
  19. }
  20. }

执行 javac builder/*.java 命令进行编译,然后再执行 java builder.TestSimpleBuilder 命令运行测试类(大家一定要自己动手运行哦,只有自己实际去运行了才会更能体会其中的思想)。
建造者/Builder - 图1
这就是一个简单的建造者模式,测试代码中的 1 2 3 4 四行代码可以随意搭配或者取消,这就是建造者模式的灵活性,但是我们可以发现,这里面是直接面向了具体的建造者来编程,在以后的扩展会比较困难,而我们一般都是选择面向抽象编程,所以接下来需要再看一下面向抽象的标准建造者模式写法。

  • 建立一个抽象的建造者类 HomeworkBuilder.java。
  1. package builder;
  2. public abstract class HomeworkBuilder {
  3. public abstract HomeworkBuilder buildEasyQc(String easyQc);
  4. public abstract HomeworkBuilder buildNormalQc(String normalQc);
  5. public abstract HomeworkBuilder buildMediumQc(String mediumQc);
  6. public abstract HomeworkBuilder buildHardQc(String hardQc);
  7. public abstract Homework build();
  8. }

这个类和之前一样都是分别为四个属性提供了一个方法,区别是这些方法都返回了建造者本身,这是为了实现后面的链式写法。

  • 新建一个具体的建造者类 ConcreateBuilder.java 继承抽象建造者类 HomeworkBuilder。
  1. package builder;
  2. public class ConcreateBuilder extends HomeworkBuilder {
  3. private Homework homework;
  4. public ConcreateBuilder(Homework homework) {
  5. this.homework = homework;
  6. }
  7. @Override
  8. public HomeworkBuilder buildEasyQc(String easyQc) {
  9. homework.setEasyQc(easyQc);
  10. return this;
  11. }
  12. @Override
  13. public HomeworkBuilder buildNormalQc(String normalQc) {
  14. homework.setNormalQc(normalQc);
  15. return this;
  16. }
  17. @Override
  18. public HomeworkBuilder buildMediumQc(String mediumQc) {
  19. homework.setMediumQc(mediumQc);
  20. return this;
  21. }
  22. @Override
  23. public HomeworkBuilder buildHardQc(String hardQc) {
  24. homework.setHardQc(hardQc);
  25. return this;
  26. }
  27. @Override
  28. public Homework build() {
  29. return homework;
  30. }
  31. }

最后新建一个测试类 TestBuilder.java 来测试一下。

  1. package builder;
  2. public class TestBuilder {
  3. public static void main(String[] args) {
  4. Homework homework = new Homework();
  5. HomeworkBuilder homeworkBuilder = new ConcreateBuilder(homework);
  6. homeworkBuilder.buildEasyQc("我是一道简单题目")
  7. .buildNormalQc("我是一道标准难度题目")
  8. .buildMediumQc("我是一道中等难度题目")
  9. .buildHardQc("我是一道高难度题目");
  10. homework = homeworkBuilder.build();
  11. StringBuilder sb = new StringBuilder();
  12. sb.append(null == homework.getEasyQc() ? "" : homework.getEasyQc() + ",");
  13. sb.append(null == homework.getNormalQc() ? "" : homework.getNormalQc() + ",");
  14. sb.append(null == homework.getMediumQc() ? "" : homework.getMediumQc() + ",");
  15. sb.append(null == homework.getHardQc() ? "" : homework.getHardQc());
  16. System.out.println("我的家庭作业有:" + sb.toString());
  17. }
  18. }

再次执行 javac builder/*.java 命令进行编译,然后再执行 java builder.TestBuilder 命令运行测试类(大家一定要自己动手运行哦,只有自己实际去运行了才会更能体会其中的思想)。

适用场景

建造者模式适用于一个具有较多属性的复杂产品创建过程,而且产品的各个属性还需要支持动态变化,但属性的种类却总体稳定的场景。所以建造者和工厂模式的区别也很明显,工厂模式是返回一个完整的产品,不支持自由组合属性,而建造者更加灵活,完全可以由使用者自由搭配从而使同一个建造者可以建造出不同的产品。

建造者模式优点

  1. 封装性好,创建和使用分离。
  2. 扩展性好,各个建造类之间相互独立,实现了解耦。

    建造者模式缺点

  3. 创建产品过程中会产生多余的 Builder 对象。

  4. 产品内部发生变化时,建造者都需要修改,成本较大,所以建造者模式的前提需要产品总体稳定,细节自由搭配的场景。