设计模式系列-Builder模式(高效构建参数)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/projim_tao/article/details/102512417

引言

在Java编程中,常常需要为一个Bean构建成员变量或者构建参数,常用的方法有使用构造函数、使用JavaBean的set()方法,但是这两个方案或多或少都存在一定的缺点,于是今天的主角builder模式出场了,它解决了这种典型应用场景的问题,采用简洁明了的使用方式,灵活多变的链式调用,使得多个参数的Bean的构建变得十分简洁。

本文不想以传统的类图的形式来讲解,而是从实际例子来看builder模式到底是什么?如何使用?

以下使用一个Student Bean来进行举例说明。

一、传统的构造函数

传统方式都是使用构造函数在构建Bean,例如存在一个学生类Student,包含id,name,age,height几个成员变量,其中idname是必须的,ageheight是可选的。如果使用传统的构造函数,一般情况下编码如下

public class Student {

    private final String name;
    private final int id;
    private final int age;
    private final int height;

    public Student(String name,int id,int age, int height){
        this.name = name;
        this.id = id;
        this.age = age;
        this.height = height;
    }

    public Student(String name,int id) {
        this(name,id,0,0);
    }

    public Student(String name,int id,int age){
        this(name,id,age,0);
    }
    


    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    public int getHeight() {
        return height;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
    
}

存在的问题主要有两个

  1. 构造函数众多,显得十分繁琐,如果参数继续增加,则构造函数会更多
  2. 无法很好地区分参数,例如public Student(String name,int id,int age),可选地参数是age,但是从构造函数上很难看出来,并且由于height与age一样都是int类型,所以不能出现public Student(String name,int id,int age),也就是没办法实现只传入一个age或者一个height参数地情况,而是不得不使用public Student(String name,int id,int age, int height),即便我只是想设置一个height不想设置age,我也必须为这个age字段设置一个默认值。

二、JavaBean setter方法

为了解决以上问题,又一个经典地方案出现了,这就是JavaBean的setter方法,需要构造一个参数为空的构造函数,所有的成员变量通过setter方法进行设置。

public class Student {

    private  String name;
    private  int id;
    private  int age;
    private  int height;

    public Student(){
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    public int getHeight() {
        return height;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                ", height=" + height +
                '}';
    }

}

这个方法也有比较明显的缺点,首先参数多的时候就需要调用很长的一系列setter方法,另外无法区分哪些是必须设置的成员变量,哪些是可选的成员变量,并且成员变量多的时候很容易漏掉一些setter,并且难以检查出来,很容易造成错误。

三、Builder模式

使用Builder模式使得这一切变得更加简洁,方便使用,先来看看Builder模式下如何编码

package tech.liujintao.leetcode;

public class Student {

    private final String name;
    private final int id;
    private final int age;
    private final int height;

    private Student(StudentBuilder studentBuilder) {
        this.name=studentBuilder.name;
        this.id =studentBuilder.id;
        this.age = studentBuilder.age;
        this.height = studentBuilder.height;
    }

    public static class StudentBuilder
    {
        private String name;
        private int id;
        private int age;
        private int height;

        public StudentBuilder(String name,int id)
        {
            this.name = name;
            this.id = id;
        }

        public StudentBuilder age(int age)
        {
            this.age=age;
            return this;
        }

        public StudentBuilder height(int height)
        {
            this.height = height;
            return this;
        }

        public Student build()
        {
            return new Student(this);
        }
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

    public int getHeight() {
        return height;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                ", height=" + height +
                '}';
    }

    public static void main(String[] args) {
        Student student = new StudentBuilder("codingway",111).age(18).height(2).build();
        System.out.println(student.toString());
    }
}

首先将Student类中的成员变量都声明为private final,并且将构造函数也声明为private,这样子就保证了无法通过Student的构造函数在构建Student Bean,并且成员变量也无法被修改,对外只提供getter方法用于或者成员变量。

随后构造一个StudentBuilder类,StudentBuilder类是一个静态类 ,其构造函数可以设置为Student必须的属性,例如id和name,其他可选的变量放到方法中,例如age和height,每个方法返回StudentBuilder本身。

Student对象只有一个构造函数,其参数就是StudentBuilder,于是所有成员变量地设置由StudentBuilder接管了,而StudentBuilder控制了哪些成员变量必须赋值,哪些是可选的,最后通过build方法构造Student Bean。使用者一目了然,链式调用更加清晰。

这就是Builder模式被大家所喜爱,并且在很多著名的开源项目中被采用的原因,特别是在一些需要配置环境参数并且参数众多的场景下。

更多内容

猜你喜欢

转载自blog.csdn.net/projim_tao/article/details/102512417