A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

【转载】        https://blog.csdn.net/lms1719/article/details/70741691
前言

在《Effective Java 第2版》中有提到,遇到多个构造器参数时要考虑使用构建器(Builder模式)。相比于重叠构造器(telescoping constructor)模式和JavaBeans模式,Builder模式实现的对象更利于使用。
下面从一个Person例子进行分析以上三种设计模式的使用,Person类有两个必要参数(id和name),有5个可选参数(age,sex,phone,address和desc)

1 重叠构造器模式

我们先来看看程序员一向习惯使用的重叠构造器模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有的可选参数。下面看看其编程实现:

/** * 使用重叠构造器模式 */public class Person {    //必要参数    private final int id;    private final String name;    //可选参数    private final int age;    private final String sex;    private final String phone;    private final String address;    private final String desc;    public Person(int id, String name) {        this(id, name, 0);    }    public Person(int id, String name, int age) {        this(id, name, age, "");    }    public Person(int id, String name, int age, String sex) {        this(id, name, age, sex, "");    }    public Person(int id, String name, int age, String sex, String phone) {        this(id, name, age, sex, phone, "");    }    public Person(int id, String name, int age, String sex, String phone, String address) {        this(id, name, age, sex, phone, address, "");    }    public Person(int id, String name, int age, String sex, String phone, String address, String desc) {        this.id = id;        this.name = name;        this.age = age;        this.sex = sex;        this.phone = phone;        this.address = address;        this.desc = desc;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

从上面的代码中,当你想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:

Person person = new Persion(1, "李四", 20, "男", "18800000000", "China", "测试使用重叠构造器模式");
  • 1

这个构造器调用通常需要许多你本不想设置的参数,但还是不得不为它们传递值。
一句话:重叠构造器可行,但是当有许多参数的时候,创建使用代码会很难写,并且较难以阅读。

2 JavaBeans模式

遇到许多构造器参数的时候,还有第二种代替办法,即JavaBeans模式。在这种模式下,调用一个无参构造器来创建对象,然后调用setter办法来设置每个必要的参数,以及每个相关的可选参数:

/** * 使用JavaBeans模式 */public class Person {    //必要参数    private int id;    private String name;    //可选参数    private int age;    private String sex;    private String phone;    private String address;    private String desc;    public void setId(int id) {        this.id = id;    }    public void setName(String name) {        this.name = name;    }    public void setAge(int age) {        this.age = age;    }    public void setSex(String sex) {        this.sex = sex;    }    public void setPhone(String phone) {        this.phone = phone;    }    public void setAddress(String address) {        this.address = address;    }    public void setDesc(String desc) {        this.desc = desc;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

这种模式弥补了重叠构造器模式的不足。创建实例很容易,这样产生的代码读起来也很容易:

Person person = new Person();person.setId(1);person.setName("李四");person.setAge(20);person.setSex("男");person.setPhone("18800000000");person.setAddress("China");person.setDesc("测试使用JavaBeans模式");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

遗憾的是,JavaBeans模式自身有着很重要的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。

3 Builder模式(推荐)幸运的是,还有第三种替代方法,既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那么好的可读性。这就是Builder模式的一种形式,不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的builder方法来生成不可变的对象。这个builder是它构建类的静态成员类。下面就是它的示例:/** * 使用Builder模式 */public class Person {    //必要参数    private final int id;    private final String name;    //可选参数    private final int age;    private final String sex;    private final String phone;    private final String address;    private final String desc;    private Person(Builder builder) {        this.id = builder.id;        this.name = builder.name;        this.age = builder.age;        this.sex = builder.sex;        this.phone = builder.phone;        this.address = builder.address;        this.desc = builder.desc;    }    public static class Builder {        //必要参数        private final int id;        private final String name;        //可选参数        private int age;        private String sex;        private String phone;        private String address;        private String desc;        public Builder(int id, String name) {            this.id = id;            this.name = name;        }        public Builder age(int val) {            this.age = val;            return this;        }        public Builder sex(String val) {            this.sex = val;            return this;        }        public Builder phone(String val) {            this.phone = val;            return this;        }        public Builder address(String val) {            this.address = val;            return this;        }        public Builder desc(String val) {            this.desc = val;            return this;        }        public Person build() {            return new Person(this);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

注意Person是不可变得,所有的默认参数值都单独放在一个地方。builder的setter方法返回builder本身。以便可以把连接起来。下面是客户端使用代码:

/** * 测试使用 */public class Test {    public static void main(String[] args) {        Person person = new Person.Builder(1, "张三")                .age(18).sex("男").desc("测试使用builder模式").build();        System.out.println(person.toString());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这样的客户端代码很容易编写,更为重要的是,易于阅读。

本文是参照《Effective Java 第2版》第二章创建和销毁对象第2条(遇到)所做的学习记录笔记,本书里面还有很多非常不错的Java高效编程方法,有兴趣的可以找来看看。



3 个回复

倒序浏览
奈斯,加油加油
回复 使用道具 举报
奈斯,加油加油
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马