第2条:遇到多个构造器参数时,考虑用构建者Bulider

 

静态工厂和构造器有个共同的局限性:他们都不能很好的扩展大量的可选参数,
比如包装食品营养成分标签,含量,卡路里,等等,有可能会超过20个可选域,对于这样的类,应该用那种构造器或静态方法来写呢,

构造器:
程序员一般习惯采用重载构造器来实现,第一个构造器,提供一个必要参数,第二个构造器提供,一个必要参数,一个可选参数,依次类推.

public NutritionFacts(int servingSize){}
public NutritionFacts(int servingSize,int calories){}
public NutritionFacts(int servingSize,int calories,int fat){}

缺点
构造器调用通常需要许多本不想设置参数,但是不得不为他们传递值.随着参数数目的增加,就很快失去了控制。

简而言之,可伸缩构造器是可行,只是当有很多参数时,会让客户端代码很难编写,而且代码也很难阅读。

另一种可选的方案就是采用JavaBeans模式。

若采用这种模式,则先调用无参构造器来创建一个对象,然后分别调用不同的setter方法来设置必要参数和可选参数:
这种模式下调用一个午餐构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个可选参数.

public class NutritionFacts{
private int servingSize=-1;
private int servings=-1;
private int calories=0;
...getter setter省略
}

优点:弥补了重载构造器模式的不足,创建实例很容易,代码易读性强

NutritionFacts cocaCola=new NutritionFacts();
cocaCola.setServingSize(22);
cocaCola.setServings(22);
...

不幸的是,JavaBeans模式也有一些严重的缺陷。由于构造过程被分到了多个调用中,一个JavaBean在其构造过程中可能处于不一致的状态。类无法仅仅通过检查构造器参数的有效性来保证一致性。试图使用一个处于不一致状态的对象将会导致失败,而且这种失败远不像那些包含bug的代码,因此它调试起来非常困难。与此相关的另一个缺点是,JavaBeans模式阻止了把类做成不可变的可能性,这需要程序员付出额外的努力来确保线程安全。

还有第三种方案,而且这种方案结合了可伸缩构造器模式的安全性和JavaBeans模式的可阅读性。它就是Builder模式。

构建器模式:

 1 public class NutritionFacts {
 2 
 3     private final int servingSize;
 4     private final int servings;
 5     private final int calories;
 6     private final int fat;
 7     private final int sodium;
 8     private final int carbohydrate;
 9 
10     public static class Bulider{
11         private  int servingSize;
12         private  int servings;
13         private  int calories=0;
14         private  int fat=0;
15         private  int sodium=0;
16         private  int carbohydrate=0;
17 
18         public Bulider( int servingSize,int servings) {
19             this.servings=servings;
20             this.servingSize=servingSize;
21         }
22         public Bulider calories( int var) {
23             this.calories=var;
24             return this;
25         }
26         public Bulider fat( int var) {
27             this.fat=var;
28             return this;
29         }
30         public Bulider sodium( int var) {
31             this.sodium=var;
32             return this;
33         }
34         public Bulider carbohydrate( int var) {
35             this.carbohydrate=var;
36             return this;
37         }
38 
39         public NutritionFacts bulid() {
40             return new NutritionFacts(this);
41         }
42     }
43     private NutritionFacts(Bulider bulider) {
44         servings=bulider.servings;
45         servingSize=bulider.servingSize;
46         calories=bulider.calories;
47         fat=bulider.calories;
48         sodium=bulider.sodium;
49         carbohydrate=bulider.carbohydrate;
50     }
51 
52 
53 }
54 //客户端测试
55 @Test
56 public void codeTest() {
57     NutritionFacts nutritionFacts=new NutritionFacts.Bulider(240, 8).
58     calories(100).sodium(33).carbohydrate(22).bulid();
59 }
优势: 

Builder模式很适合于类的层级结构。对于多个平行类,可以平行使用每个类对应的builder。抽象的类拥有抽象的builder

相较于构造器,builder的一个小优点是,builder能拥有多个可变参数,因为每一个参数都是在它自己的方法里指定的。或者,builder能将传入到不同方法里的参数聚合起来然后传入单个域里

Builder模式很灵活。单个builder能被用来构建多个对象。我们可以在多次的build方法之间对builder的参数进行调整,以此来改变创建的对象。builder可以在创建对象时自动填充一些值,

比如序列号,每次一个新的对象被创建时,builder都能自动对其值进行增加。
不足:
为了创建对象,必须先创建它的构建器,虽然创建构建器的开销在实践中可能不那么明显,但是在某些注重性能的情况下,可能就成问题,Bulider模式比重叠构造器模式更加冗长,

而且,假如一开始是用构造器或者静态工厂,但是随着类的演变,参数的数量开始失控时,若此时想转用builder,那些过时的构造器或者静态工厂将会显得有点尴尬。因此,最好一开始就使用builder。

posted @ 2017-09-21 21:26  webzom  阅读(103)  评论(0)    收藏  举报