Effective Java 2.2——遇到多个构造器参数时要考虑用构建器

第二条 遇到多个构造器参数时要考虑用构建器

大家还记得第一条的静态工厂吗?静态工厂来代替构造器有4个优点:自定义方法名;避免创建过多的实例;隐藏子类的实现细节;在泛型类里面可以节约客户端代码。
这一篇博客我们就来了解一下构建器。什么是构建器,可能大家没听说过这个名字。但我相信大家肯定在自己写代码或者看别人的代码的时候看到过build。
比如A a = new A.builder().aa().aaa().aaaa().build();这个就是我们今天要讲的构建器。提出构建器是为了弥补静态工厂和构造器共同的局限性:不能很好的扩展到大量的可选参数。

第一种重叠构造器模式:

class A
{
    int a;
    int aa;
    int aaa;
    int aaaa;
    A()
    {

    }
    A(int a)
    {
        this.a = a;
    }
    A(int a,int aa)
    {
        this(a);
        this.aa = aa;
    }
    A(int a,int aa,int aaa)
    {
        this(a,aa);
        this.aaa = aaa;
    }
    A(int a,int aa,int aaa,int aaaa)
    {
        this(a, aa, aaa);
        this.aaaa = aaaa;
    }
}

A类有四个可参数,那么我们写构造函数的话就要写4个加上空构造函数则为5个(我们这里还没有考虑排列组合。。)。那一旦参数变成50个或者更多,我们写构造函数的量就大大增加,而且基本代码都是一模一样的。还有一点,我们在写客户端的时候想初始化a和aaa,但我们没有编写这个特定的构造器,那我们只能A a = new A(1,0,1,0);平白无故要多写两个0。这点当参数增加的时候,客户端代码也很难写。

书中说道:重叠构造器模式可行,但是当有许多参数时,客户端代码会很难编写,并且仍然较难以阅读。因为我传进去那么多个0,我都不知道这些0是干嘛的。

另外一种模式为JavaBeans模式:

class A
{
    int a;
    int aa;
    int aaa;
    int aaaa;
    public void setA(int a)
    {
        this.a = a;
    }
    public void setAA(int aa)
    {
        this.aa = aa;
    }
    public void setAAA(int aaa)
    {
        this.aaa = aaa;
    }
    public void setAAAA(int aaaa)
    {
        this.aaaa = aaaa;
    }
}

这里我们把各个参数写成各个set方法,这样子我们在客户端实例化对象的时候就可以用什么参数set什么参数。这看起来非常不错,而且可读性大大增加。但是JavaBeans自身有严重的缺点:不能保证对象的一致性。
我们复写一个toString方法用于输出参数的情况:

    public String toString()
    {
        return "a="+a+" aa="+aa+" aaa="+aaa+" aaaa="+aaaa;
    }

在客户端里面这么写:

        A a = new A();
        System.out.println(a.toString());
        a.setA(1);
        System.out.println(a.toString());

输出结果为:
a=0 aa=0 aaa=0 aaaa=0
a=1 aa=0 aaa=0 aaaa=0
大家可以很明显看到这个对象是不安全的,尤其是在多线程环境下。如果要用JavaBeans模式,我们就应该多一些额外的努力来保证其线程安全。

接下来介绍我们的构建器模式:

public class Main {
    public static void main(String args[]) {
        A a = new A.ABuilder().addA(1).addAA(2).addAAAA(4).build();
        System.out.println(a.toString());
    }
}
class A
{
    int a;
    int aa;
    int aaa;
    int aaaa;
    public static class ABuilder
    {
        int a;
        int aa;
        int aaa;
        int aaaa;
        public ABuilder addA(int a)
        {
            this.a = a;
            return this;
        }
        public ABuilder addAA(int aa)
        {
            this.aa = aa;
            return this;
        }
        public ABuilder addAAA(int aaa)
        {
            this.aaa = aaa;
            return this;
        }
        public ABuilder addAAAA(int aaaa)
        {
            this.aaaa = aaaa;
            return this;
        }

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

    private A(ABuilder aBuilder) {
        a = aBuilder.a;
        aa = aBuilder.aa;
        aaa = aBuilder.aaa;
        aaaa = aBuilder.aaaa;
    }
    public String toString()
    {
        return "a="+a+" aa="+aa+" aaa="+aaa+" aaaa="+aaaa;
    }
}
  1. 先在A内部定义一个构建器的内部类ABuilder,并把A的参数一模一样在ABuilder里面写一份。
  2. 然后在ABuilder里面写一些设置参数的方法,不设置即为默认值。注意这些返回值为ABuilder,这样子写能帮我们节约一些代码,如A a = new A.ABuilder().addA(1).addAA(2).addAAAA(4).build();
  3. 在A类里面写一个构造方法,传入ABuilder的对象,并把该对象里面的参数拿出来赋值给A。
  4. 在ABuilder里面写一个build()方法,返回一个新的A对象,return new A(this);其实就是调用了我们第三步写的那个构造方法,传入this就是把当前的ABuilder实例传进去。

我想我应该已经写的蛮详细了吧~

总结:

构建器模式既能有构造器那样的安全性,也可以有JavaBeans的可读性,在多个参数的时候比较实用。在参数少的时候就代码量相比构造器多一些。还有一点有必要提及的是,大家会发现如果我们使用构建器的时候,如果要创建一个对象比如A,那么我们就必须先创建一个构建器对象ABuilder。这一步操作会有时间的开销,在一些特别注重效率和速度的程序里,这个可能会造成一些问题。但在大部分程序里面,我们都是可以不计这一点的时间开销的~
所以如果你编写的类需要有很多参数时,请优先考虑构建器模式!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值