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

静态工厂和构造函数共同的局限性:不能很好地适应大量的可选参数。

重叠构造函数模式

第一个只有必要参数的构造函数,
第二个构造函数有一个可选参数,
第三个构造函数有两个可选参数。
最后一个构造函数包含所有可选参数

r

重叠构造函数的缺点

这个构造函数有许多不想设置的参数,但是还是不得不为它们传值。

随着参数数目的增加,很快就会失控。

重叠构造函数模式可行,但是当有许多参数的时候,客户端代码就很难编写,并且仍然较难以阅读。

如果读者想要知道哪些值是什么意思,必须很仔细地数着这些参数来探个究竟。
一长串类型相同的参数会导致一些微妙的错误。如果客户端不小心颠倒了其中两个参数的顺序,编译器也不会报错,但是在程序运行的时候会出现错误的行为。

JavaBeans模式

调用一个无参数的构造函数来创建对象,然后调用set方法来设置每个必要的参数,以及每个相关的可选参数。


这种模式弥补了重叠构造函数模式的不足:代码容易阅读。

JavaBeans模式的不足

构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造函数参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与包含错误的代码是不同的,调试起来非常困难。

JavaBeans模式阻止了把类做成不可变类的可能,这需要程序员付出额外的努力来保证它的线程安全。

JavaBeans模式不足的解决办法

思路:对象构造完成,不允许在解冻之前使用
通过手工“冻结”对象,可以弥补这些不足,但是这种方式十分笨拙,在实践中很少使用。此外,它甚至会在运行时导致错误,因为编译器无法确保程序员会在使用之前先在对象上调用freeze方法。

Builder模式

保证像重叠构造函数模式那样的安全性,也能保证像JavaBeans模式那么好的可读性。

不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造函数(或静态工厂),得到一个builder对象。

然后客户端在builder对象上调用类似于set的方法,来设置每个相关的可选参数。
最后客户端调用无参的build方法来生成不可变的对象。

builder是它构建的类的静态成员类。

注意NutritionFacts是不可变的,所有的默认参数值都单独放在一个地方。builder的setter方法返回builder本身,以便可以链式调用。


这样的客户端代码很容易编写和阅读。builder模式模拟了具名的可选参数。

builder像个构造函数一样,可以对其参数强加约束条件。build方法可以检验这些约束条件,将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行校验,这一点很重要。
如果违反了任何约束条件,build方法就应该抛出IllegalStateException异常。

异常的详细信息应该显示出违反了那个约束条件。

Builder模式对多个参数强加约束条件的方法

用多个set方法对某个约束条件必须持有的所有参数进行检查。如果该约束条件没有得到满足,set方法就会抛出IllegalArgumentException。
这样有个好处,就是一旦传递了无效的参数,立即就会发现约束条件无效,而不是等着调用build方法。

builder模式与构造函数的对比

builder模式可以有多个可变的参数(多个set方法)。构造函数只能有一个可变参数。

builder模式灵活性

可以利用单个builder构建多个对象。
builder的参数可以在创建对象期间进行调整,也可以随着不同的对象而改变。builder可以自动填充某些域,例如每次创建对象时自动增加序列号。

builder是一个抽象工厂

设置了参数的builder是一个很好的抽象工厂。
客户端可以将这样的一个builder传给方法,使该方法能够为客户端创建一个或多个对象。要这样使用,必须有一个类型来表示builder。

NutritionFacts.Builder类可以实现Builder<NutritionFacts>接口。

带有Builder实例的方法利用有限制的通配符类型来约束构建器的类型参数。

Java传统的抽象工厂

Java传统的抽象工厂是Class对象,用newInstance方法充当build方法的一部分。
这种用法隐含着许多问题:
newInstance方法总是企图调用类的无参构造函数,这个构造函数甚至可能根本不存在。如果类没有可以访问的无参构造函数,也不会收到编译时错误。相反,客户端代码必须在运行时处理InstantiationException或IllegalAccessException,这样既不雅观也不方便。

newInstance方法还会传播由无参构造函数抛出的任何异常。

Class.newInstance破坏了编译时的异常检查。Builder接口弥补了这些不足。

Builder模式的不足

为了创建对象,必须先创建构建器。

builder模式比重叠构造函数模式更加冗长,因此它只在有很多参数的时候才使用(四个以上)。
但是如果预测到类将会添加更多的参数,可以一开始就使用构建器,这样就不会有过时的构造函数或静态工厂。

总结

如果类的构造函数或静态工厂中具有多个参数,设计这种类时,Builder模式是不错的选择。
与传统的重叠构造函数模式相比,使用Builder模式代码更易于阅读和编写,构建器比JavaBeans更加安全。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容