架构师内功心法,注重方法调用顺序的建造者模式详解

建造者模式适用于创建对象需要很多步骤,但是步骤的顺序不是固定不变的。先看一下建造者模式的类图:

建造者模式中的四个重要角色:

  • 产品(Product):要创建的产品类对象
  • 抽象建造者(Builder):规范产品对象的各个组成部分的建造
  • 建造者(Concrete Builder):具体化对象的各个组成部分的创建
  • 调用者(Director):负责保证对象各部分完整创建或按某种顺序创建

二、建造者模式的应用场景

建造者模式适用于一个具有较多的零件的复杂产品的创建过程,由于需求的变化,组成这个复杂产品的各个零件经常猛烈变化,但是它们的组合方式却相对稳定。

建造者模式适用于以下几种场景:

  • 相同的方法,不同的执行顺序,产生的结果也不同
  • 多个部件或零件,装配到一个对象中,产生的结果不同
  • 产品类复杂,或者产品类中调用顺序不同产生不同的作用
  • 初始化对象特别复杂,参数多,而且很多参数都有默认值

2.1 建造者模式的基本写法

我们以公司的技术培训为例,一个完整的技术培训需要由发布培训通知、制作培训PPT、组织员工培训、现场(远程)培训、提交培训问卷等步骤。下面我们用建造模式的代码来简单实现这类场景,首先创建一个技术培训的 TechnicalTraining 产品类 :

public class TechnicalTraining {

    private String notice;
    private String ppt;
    private String training;
    private String questionnaire;

    public String getNotice() {
        return notice;
    }

    public void setNotice(String notice) {
        this.notice = notice;
    }

    public String getPpt() {
        return ppt;
    }

    public void setPpt(String ppt) {
        this.ppt = ppt;
    }

    public String getTraining() {
        return training;
    }

    public void setTraining(String training) {
        this.training = training;
    }

    public String getQuestionnaire() {
        return questionnaire;
    }

    public void setQuestionnaire(String questionnaire) {
        this.questionnaire = questionnaire;
    }

    @Override
    public String toString() {
        return "TechnicalTraining{" +
                "notice="" + notice + """ +
                ", ppt="" + ppt + """ +
                ", training="" + training + """ +
                ", questionnaire="" + questionnaire + """ +
                "}";
    }
    
}

接着创建建造者 TrainingBuilder 类,将复杂的构造过程封装起来:

public class TrainingBuilder {

    private TechnicalTraining technicalTraining = new TechnicalTraining();

    /**
     * 发布培训通知
     * @param notice
     */
    public void PostNotice(String notice) {
        technicalTraining.setNotice(notice);
    }

    /**
     * 制作培训PPT
     */
    public void createPPT(String ppt) {
        technicalTraining.setPpt(ppt);
    }

    /**
     * 组织员工培训
     */
    public void organizeTraining(String training) {
        technicalTraining.setTraining(training);
    }

    /**
     * 提交培训问卷
     */
    public void sumitQuestionnaire(String questionnaire) {
        technicalTraining.setQuestionnaire(questionnaire);
    }
    
     public TechnicalTraining build() {
        return technicalTraining;
    }

}

测试main方法:

public static void main(String[] args) {

TrainingBuilder builder = new TrainingBuilder();
builder.PostNotice("发布培训通知");
builder.createPPT("创建ppt");
builder.organizeTraining("组织员工培训");
builder.sumitQuestionnaire("提交培训问卷");
System.out.println(builder.build());

}

最后来看一下类图:

2.2 建造者模式的链式写法

在平时的应用中,建造者模式通常是采用链式编程的方式构造对象,下面我们来改造上面的案例代码,将TechnicalTraining变成TrainingBuilder的内部类,将构造步骤添加进去,每完成一个步骤,都返回 this:

public class TrainingBuilder {

    public class TechnicalTraining {

        private String notice;
        private String ppt;
        private String training;
        private String questionnaire;

        public String getNotice() {
            return notice;
        }

        public void setNotice(String notice) {
            this.notice = notice;
        }

        public String getPpt() {
            return ppt;
        }

        public void setPpt(String ppt) {
            this.ppt = ppt;
        }

        public String getTraining() {
            return training;
        }

        public void setTraining(String training) {
            this.training = training;
        }

        public String getQuestionnaire() {
            return questionnaire;
        }

        public void setQuestionnaire(String questionnaire) {
            this.questionnaire = questionnaire;
        }
    }

    private TechnicalTraining technicalTraining = new TechnicalTraining();

    /**
     * 发布培训通知
     * @param notice
     */
    public TrainingBuilder PostNotice(String notice) {
        technicalTraining.setNotice(notice);
        return this;
    }

    /**
     * 制作培训PPT
     */
    public TrainingBuilder createPPT(String ppt) {
        technicalTraining.setPpt(ppt);
        return this;
    }

    /**
     * 组织员工培训
     */
    public TrainingBuilder organizeTraining(String training) {
        technicalTraining.setTraining(training);
        return this;
    }

    /**
     * 提交培训问卷
     */
    public TrainingBuilder sumitQuestionnaire(String questionnaire) {
        technicalTraining.setQuestionnaire(questionnaire);
        return this;
    }

    public TechnicalTraining build() {
        return this.technicalTraining;
    }

}

测试main方法:

 public static void main(String[] args) {

    TrainingBuilder builder = new TrainingBuilder();
    builder.PostNotice("发布培训通知")
    .createPPT("创建ppt")
    .organizeTraining("组织员工培训")
    .sumitQuestionnaire("提交培训问卷");
    System.out.println(builder.build());
}

最后再来看下类图:

三、建造者模式在源码中的体现

3.1 StringBuilder类

使用StringBuilder类,我们常用的有append()、toString()方法,我们来看下append()方法的源码:

public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}

@Override
public StringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

3.2 Spring中的BeanDefinitionBuilder类

比如 BeanDefinitionBuilder 通过调用 getBeanDefinition()方法获得一个 BeanDefinition 对象,比如下面的源码:

private BeanDefinitionBuilder(AbstractBeanDefinition beanDefinition) {
    this.beanDefinition = beanDefinition;
}

public AbstractBeanDefinition getRawBeanDefinition() {
    return this.beanDefinition;
}

public AbstractBeanDefinition getBeanDefinition() {
    this.beanDefinition.validate();
    return this.beanDefinition;
}

四、建造者模式的优缺点

建造者模式的优点:

  • 封装性好,创建和使用分离;

  • 扩展性好,建造类之间独立、一定程度上解耦。

建造者模式的缺点:

  • 产生多余的 Builder 对象;

  • 产品内部发生变化,建造者都要修改,成本较大。

建造者模式和工厂模式的区别

通过前面的学习,我们已经了解建造者模式,那么它和工厂模式有什么区别呢? 1、建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象。

2、创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的都一样。

3、关注重点不一样,工厂模式模式只需要把对象创建出来就可以了,而建造者模式中不仅要创建出这个对象,还要知道这个对象由哪些部件组成。

4、建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。