重叠构造器+JAVABeans模式->Builder模式
来源:互联网 发布:大学生借钱软件排名 编辑:程序博客网 时间:2024/06/06 14:01
考虑一个类表示包装食品的营养成分标签,这些标签有些是必要的,如每份的含量,还有超过20个可选域:卡路里,蛋白质,总脂肪量,饱和脂肪量,胆固醇,钠,钙等等。大多数产品在可选域中都会有非零值。
对于这样的一个需要用到很多域的类时,我们应该采用什么构造器来构造类对象呢?大部分程序员习惯采用重叠构造器模式:(简化起见只显示1个必要域和3个可选域)
//重叠构造器public class Temp { private int p1;//必要参数 private int p2; private int p3; private int p4; public Temp(int p1){ this.p1 = p1;} public Temp(int p1,int p2){ this.p1=p1; this.p2=p2;} public Temp(int p1,int p2, int p3){ this.p1=p1; this.p2=p2; this.p3=p3; } public Temp(int p1,int p2,int p3,int p4){ this.p1=p1; this.p2=p2; this.p3=p3;this.p4=p4; }}
//生成对象实例Temp t3 = new Temp(1,0,0,2);
这个构造器调用通常需要许多你本来不想设置的参数,如上面构造的例子中,我只想设置P4可选域,可是我必须给p2,p3设置一个0值,如果仅仅3个参数看起来还不算太糟,但是当参数数目增加到20个的时候,你会觉得没办法控制。并且这样的构造器比较难以阅读,因为可选域的类型是相同的,在对类的参数构造不熟悉的情况下,很难选择适当的构造器,而且参数传递错误的时候,这样的错误不会被编译器发现,但却会导致运行错误。这个时候,我们还有第二种代替的方法,即JAVABeans模式,在这种模式下,调用一个必要参数构造器来创建对象,然后调用setter方法设置每个可选参数:
//JAVABean模式 public class Temp2 { private int p1;//必要参数 private int p2; private int p3; private int p4; public Temp2(int p1){this.p1 = p1; } public void setP1(int p1){ this.p1 = p1; } public void setP2(int p2){ this.p2 = p2; } public void setP3(int p3){ this.p3 = p3; } public void setP4(int p4){ this.p4 = p4; }}Temp2 t1 = new Temp2(1);t1.setP2(2);t1.setP4(4);
哇,这样看起来又方便又便于阅读,实例创建也会很快,真的是喜大普奔啊,但是JAVABean模式有一个很严重的缺点。因为构造器过程被分到了几个调用中,在构造中JAVABean可能处于不一致的状态,因为我们只能检测对象实例是否为空,却无法检测对象域值的有效性。想象一下,在多线程的应用下当线程1执行Temp2 t1 = new Temp2(1)后,线程2刚刚在这个时候需要使用t1实例,得到的t1却是一个没有被完全构造的对象实例,导致程序出错,而且这样的错误难以调试。JAVABean模式的使用需要程序员付出额外的努力确保它的线程安全。
幸运的是,还有第三种替代方法,既能保证像重叠构造器模式那样的安全性,又能保证像JAVABeans模式那样有好的可读性。这就是Builder模式的一种形式,不直接生成想要的对象,而是让客户端利用必要参数调用构造器,得到一个builder对象,然后通过builder对象上调用类似于setter的方法,设置可选参数。最后,客户端调用无参的builde方法来生成不可变对象。(Builder是它构建的类的静态成员类):
// Builder 模式生成不可变对象public class Temp3{ private final int p1;//必要参数 private final int p2; private final int p3; private final int p4; public static class Builder{ private final int p1; private int p2; private int p3; private int p4; public Builder(int p1){this.p1 = p1; } public Builder setP2(int p2){ this.p2 = p2; } public Builder setP3(int p3)( this.p3 = p3; ) public Builder setP4(int p4){ this.p4 = p4; } public Temp3 build(){ return new Temp3(this ); } } private Temp3(Builder builder){ p1 = builder.p1; p2 = builder.p2; p3 = builder.p3; p4 = builder.p4; }}Temp3 t1 = new Temp3.Builder(1).setP2(2).setP3(3).setP4(4).build();Temp3 t2 = new Temp3.Builder(1).setP4(4).build();
Builder作为Temp3的静态成员类,可以通过对象名称获得,通过构造Builder对象实例,构造完成后调用builder方法,去实例化Temp3对象。这样的好处就是,设置参数是具名,而且可以在builder里面对参数强加约束条件,build方法可以检测这些约束条件,将参数从builder拷贝到对象中之后,并在对象域中进行检测,也可在Builder setter方法中去约束。
同时,也同样具有不足,为了创建对象,不许先创建他的构造器在性能要求很高的时候,可能会成为一个问题。但是一般程序选择这样的模式是一个不错的选择。