2. 【创建和销毁对象】用构建器创建复杂参数对象(Builder模式)

本文是《Effective Java》读书笔记的第二条。


废话不多说,先上个例子吧。我们一开始学程序开发,写完Hello world之后,通常就开始跟student较劲了。。。
学生通常有多个属性,比如学号、姓名、性别、年龄、年级、邮箱、家庭住址等等。

那么问题来了,在创建学生对象的时候若要初始化不同的参数,得弄多少个构造方法啊,这恐怕是个排列组合问题。如果用前一篇介绍到的静态工厂方法也比较麻烦。
通常大家会采用层叠构造器,如下(我真的不是为了凑字数。。。)

public class Student {

    private String id;
    private String name;
    private String sex;
    private int age;
    private String grade;
    private String email;
    private String addr;

    public Student() {
    }

    public Student(String id) {
        this(id, null);
    }

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

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

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

    public Student(String id, String name, String sex, int age, String grade) {
        this(id, name, sex, age, grade, null);
    }

    public Student(String id, String name, String sex, int age, String grade,
            String email) {
        this(id, name, sex, age, grade, email, null);
    }

    public Student(String id, String name, String sex, int age, String grade,
            String email, String addr) {
        super();
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.grade = grade;
        this.email = email;
        this.addr = addr;
    }   
}

那么问题有来了,我只想初始化姓名和邮箱用哪个?还有这么多构造器都看乱套了,顺序一不小心就搞错,有木有其他办法?
还有一种办法就是采用JavaBeans模式,先用无参构造方法来创建对象,然后调用setter方法来依次设置每个需要设置的值。
嗯~问题解决了嘛,等等,好像有什么不妥~~~

  • 由于几个参数的setter过程是分离的,因此在构造过程中有可能处于不一致的状态;
  • 如果对象是不可变的话,就无法使用setter的方式了。

铺垫了这么多,还是赶紧清楚本文主角——Builder模式吧。
使用构建器Builder,你可以这样创建对象:

Student stu = new Student.Builder("20150102", "Tom").sex("male").age(12)
    .grade("Grade 1").email("tom@163.com").addr("铁岭鸟不拉屎村").builder();

怎么样,是不是清晰了很多,而且规避了刚才JavaBeans方式的两个问题。那么如何实现刚才的效果呢?

public class Student {

    private String id;
    private String name;
    private String sex;
    private int age;
    private String grade;
    private String email;
    private String addr;

    public static class Builder {
        // 必须初始化的属性
        private String id;
        private String name;
        // 可选属性
        private String sex;
        private int age;
        private String grade;
        private String email;
        private String addr;

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

        public Builder sex(String sex) { this.sex = sex; return this; }

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

        public Builder grade(String grade) { this.grade = grade; return this; }

        public Builder email(String email) { this.email = email; return this; }

        public Builder addr(String addr) { this.addr = addr; return this; }

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

    public Student(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.sex = builder.sex;
        this.age = builder.age;
        this.grade = builder.grade;
        this.email = builder.email;
        this.addr = builder.addr;
    }
}

不用我多说,从刚才的代码你首先感觉到的就是高度的灵活性,此外:

  • Builder可以对其参数添加约束条件,也就是在build()方法中进行检验;
  • Builder可以自动填充某些域,比如每次创建对象时自动增加序列号;
  • 这种方式可以实现抽象工厂,比如下边的接口,刚才代码里的Student.Builder其实就是Builder<Student>的实现;相对于Class.newInstance()这种Java中传统的抽象工厂实现,有如下优点
    • newInstance()方法总是调用类的无参构造方法,当然也许不存在无参构造方法,这时候并没有编译错误;
    • 对于newInstance()方法客户端必须处理InstantiationException或者IllegalAccessException,而且newInstance()方法还会传递无参构造器抛出的异常,即使它本身缺乏相应的throws子句,因此它实际上破坏了编译时的异常检查。
public interface Builder<T> {
    public T buile();
}

当然Builder模式也有不足:

  • 为了创建对象必须先创建它的构建器,在某些非常注重性能的情况下,可能其开销就是问题了;
  • Builder模式相对来说,代码量更多,所以通常只有在很多参数的时候才使用,尤其是大多数参数都是可选的时候。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值