02 遇到多个构造器参数时要考虑用构建器

2017-11-01  本文已影响0人  xhanbao

静态工厂方法和构造器的共同缺点或者局限性就是:对于存在大量可选参数的类中,其扩展性都不是很好。构建器(Builder)可以比较好的解决这些问题。

重叠构造器
在介绍构建器之前先介绍下重叠构造器吧。什么是重叠的构造器呢?就是有多个构造器,重叠嘛。。。这些构造器有一些特点:至少有一个包含所有必要属性的构造器,必要属性。。。这么说还有非必要属性,是的,重叠构造器应用的一个场景就是,对于一个类来说,它其中的所有属性不一定都是必须要使用或者设置值的,举个栗子来说吧,用户在某个网站上注册的时候,有一些信息比如用户名密码是必须要填的,而有一些信息比如生日住址之类的就是非必须的。
回到正题,除了包含所有属性的构造器之外,还有多个构造器,这些构造器中至少包含必要属性和若干个非必要属性,也可以只包含必要属性哦。这些构造器其实最终都是转到了包含所有属性的构造器,只是那些未包含的非必要属性被设置成默认值了而已。光说不练非好汉,下面就直接来个栗子说明一下吧:

public class Person {
    private String name;//姓名或者用户名->必须
    private String phone;//电话->必须
    private int age;//年龄->可选
    private String address;//地址->可选

    /**
     * 包含所有属性的构造方法
     */
    public Person(String name, String phone, int age, String address) {
        this.name = name;
        this.phone = phone;
        this.age = age;
        this.address = address;
    }

    /**
     * 包含必要属性和一个可选属性的构造方法
     */
    public Person(String name, String phone, int age) {
        this(name, phone, age, "");
    }

    /**
     * 包含必要属性和一个可选属性的构造方法
     */
    public Person(String name, String phone, String address) {
        this(name, phone, 0, address);
    }

    /**
     * 包含必须属性的构造方法
     */
    public Person(String name, String phone) {
        this(name, phone, 0);
//        this(name,phone,"");//当然也可以选择这个构造方法,结果是一样的
    }
}

重叠构造器的问题:
与该方法的特点对应,如果一个类中包含很多的参数,就会写很多的构造方法,而且阅读性不高。还有啊,客户端在调用的时候需要仔细了解各个构造方法的含义,如果不小心把参数的含义看错了,或者位置颠倒了就会产生错误,而且错误还不好定位。。。下面介绍另外一种方法--JavaBean模式。

JavaBean模式
这种模式也很常用,简单来说就是类中存在一个无参的构造方法(哎呀,参考资料中都是构造器,本人比较习惯用构造方法,所以可能一会构造器一会构造方法的,请谅解。。。),然后构造方法就没有别的了,对,就是没有别的了。你可能会问,那类中的参数怎么办呢?别急,类中还存在一大堆set方法,作用就是设置参数。所以,这种方法比较好地弥补了重叠构造器的不足,创建实例的过程:

Person person=new Person();
person.setName("路飞");
person.setAge(18);
//其他的set方法。。。。

同样的,这种方法也存在缺点:并发问题,构造过程中JavaBean可能处于不一致的状态。并且,该方法阻止了把类做成不可变的可能。

前面巴拉巴拉一大堆,目的就是为了引出本文的重头戏:Builder模式。

Builder模式
上代码:

public class PersonB {
    private String name;//姓名或者用户名->必须
    private String phone;//电话->必须
    private int age;//年龄->可选
    private String address;//地址->可选

    /**
     * 包含所有属性的构造方法
     */
    private PersonB(Builder builder) {//通过Builder来构造,该方法是private类型的
        this.name = builder.name;
        this.phone = builder.phone;
        this.age = builder.age;
        this.address = builder.address;
    }

    public static class Builder {
        private String name;//姓名或者用户名->必须
        private String phone;//电话->必须

        //设置了默认值
        private int age = 0;//年龄->可选
        private String address = "";//地址->可选

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

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

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

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

}

注意:PersonB这个类的构造方法是private类型的,也就是说无法通过new PersonB()来创建实例。具体怎么用呢,来来来,敲黑板啦~

PersonB person = new PersonB.Builder("山治", "110").age(20).address("桑尼号").build();

是不是很简单,而且也很明了,传递了必要的参数和非必须的参数。同时在build()方法中可以对参数进行检验(你需要根据实际情况添加对应的检验逻辑)。
在抽象工厂中的应用:
客户端可以将builder传递给某个方法,这样该方法就能够创建一个或者多个对象了。如果可以使用泛型的话:

public interface Builder<T> {
  public T build();
}

Tree buildeTree(Builder<? extends Node> nodeBuilder) { ... }

代码中第二部分就是一个实际的应用,功能就是通过Builder来创建一棵树。。。

下面说下该方法的缺点吧:
一般来说一个方法的缺点往往与其实现方法相关,Builder模式的缺点就是,必须要先创建它的构建器Builder(这不是废话吗)。该方法可能更加冗长,比较适合参数多的情况,比如有4个或者更多的参数。

总之:如果类的构造器或者静态工厂中有多个参数,在设计这种类的时候,可以考虑Builder模式,当一个类中大多数参数都是可选的时候就更完美了~。

上一篇下一篇

猜你喜欢

热点阅读