重叠构造器+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方法中去约束。
  同时,也同样具有不足,为了创建对象,不许先创建他的构造器在性能要求很高的时候,可能会成为一个问题。但是一般程序选择这样的模式是一个不错的选择。

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大阪直美 日本大阪 大阪烧 大阪师团 大阪城 大阪城公园 大阪机场 东京大阪 大阪到东京 大阪海游馆 大阪公园 东京到大阪 大阪松 大阪攻略 大阪地铁 大阪景点 大阪府 大阪天守阁 大阪免税店 大阪樱花 大阪到奈良 大阪关西 大阪自由行 大阪水族馆 大阪美食 大阪在哪里 大阪特产 大阪在哪 大阪购物 大阪酒店 大阪民宿 大阪市 大阪天满宫 大阪心斋桥 大阪环球影城 大阪关西国际机场 大阪直美简介 大阪城天守阁 大阪城公园天守阁旅游 大阪直美为什么那么黑 大阪城公园旅游