使用Builder优雅的生成对象

字数 342阅读 249

一,产生背景:属性较多的对象,一直分开设置比较麻烦;于是产生了builder方式生成对象。

1.1,分开设置对象的属性

如:

public class Student {
private final int stuId;//必须
private final String name;//必须
private final int age;//可选
private final int gender;//可选
private final String address;//可选
public Student(int stuId,String name){
this(stuId,name,0,1,"");
}
public Student(int stuId,String name,int age){
this(stuId,name,age,1,"");
}
public Student(int stuId,String name,int age,int gender){
this(stuId,name,age,gender,"");
}
public Student(int stuId,String name,int age,int gender,String address){
this.stuId = stuId;
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
}

如此定义对象,要new一个就会是这样:

Student stu = new Student(1,hu,21);
or
Student stu1 = new Student(2,hu,21,1,shenzhen); 
or
(如果你定义了set方法)
Student stu2 = new Student();
stu2.setAge(xxx);
stu2.setGender(1);
...

看上去似乎不是很优雅,但是使用builder就可以链式设置对象参数

1.2,builder链式设置对象参数

需要你如下定义对象:

public class Student {
    private final int stuId;// 必须
    private final String name;// 必须
    private final int age;// 可选
    private final int gender;// 可选
    private final String address;// 可选

    private Student(StudentBuilder builder) {
        this.stuId = builder.stuId;
        this.name = builder.name;
        this.age = builder.age;
        this.gender = builder.gender;
        this.address = builder.address;
    }

    public int getStuId() {
        return stuId;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getGender() {
        return gender;
    }

    public String getAddress() {
        return address;
    }

    // 自动生成toString方法快捷键,鼠标右键--》source--》generateToString
    @Override
    public String toString() {
        return "Student [stuId=" + stuId + ", name=" + name + ", age=" + age + ", gender=" + gender + ", address="
                + address + "]";
    }

    public static class StudentBuilder {
        // private final int stuId;//将Student的属性用final修饰了,就一定需要先初始化
        // 而且,这里我们只提供,get方法获得属性值
        // private final String name;//将Student的属性用final修饰了

        private int stuId;// 将Student的属性用final修饰了
        private String name;// 将Student的属性用final修饰了

        private int age;
        private int gender;
        private String address;

        public StudentBuilder() {

        }

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

        public StudentBuilder setAge(int age) {
            // 将参数赋值给当前对象的属性
            this.age = age;
            // 返回当前对象
            return this;
        }

        public StudentBuilder setGender(int gender) {
            // 将参数赋值给当前对象的属性
            this.gender = gender;
            // 返回当前对象
            return this;
        }

        public StudentBuilder setAddress(String address) {
            this.address = address;
            return this;
        }

        /**
         * 最后需要一个build()方法,返回这个设置完参数的对象
         * 
         * @return
         */
        public Student build() {
            // 返回新建的这个对象
            return new Student(this);
        }
    }

}

使用时,链式设置就行,记得最后来一个build(),构建(返回)这么一个对象:

        Student student1 = new Student.StudentBuilder(1, "小明")// 必填属性在构造方法中赋值
                .setAge(1)// 设置可选属性 年龄
                .setGender(1)// 设置可选属性 性别 默认1为男
                .build();// 对象构建完毕的标识,返回Student对象

        //没有必设置的值
        Student student2 = new Student.StudentBuilder().setAge(21).setGender(2).build();

二,进阶

builder模式另一个重要特性是:它可以对参数进行合法性验证,如果我们传入的参数无效,我们可以抛出一个IllegalStateException异常,但是我们在哪里进行参数合法性验证也是有讲究的:那就是在对象创建之后进行合法性验证。我们修改StudentBuilder的build()方法

public Student build(){
            Student student = new Student(this);
            if (student.getAge()>120){
                throw  new IllegalStateException("年龄超出限制");
            }
            return student;
        }

为什么要先创建对象,再进行参数验证?因为我们的StudentBuilder是线程不安全的,如果我们先进行参数验证后创建对象,那么创建对象的时候对象的属性可能已经被其他线程改变了,例如下面的代码就是错误的

/**
         * 错误的
         */
        public Student build(){
            if (age>120){
                throw  new IllegalStateException("年龄超出限制");
            }
            return new Student(this);
        }

推荐阅读更多精彩内容