• Builder模式的思考(Effective Java)


    《Effective Java》(第2版)中第二条中提到:遇到多个构造器参数时要考虑用构建器。在复习static关键字和内部类时回头看了一下,这才明白了为什么要用静态内部类来做处理,这里记录一下。

    先看再看一下《Effective Java》书中的例子,例子中是用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个是必需的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等。如果用一般的处理方式,直接把这些参数在构造器中初始化的话,有两个缺点:
    1、没法体现营养成分的必需和非必需属性
    2、参数太多,这样在实例化传参数的时候其实比较难区分的。因为他们几乎都是int型变量,而且即使传递错了也不会有任何提醒。
    基于上面的问题,书中转而使用Builder模式,当然,Builder模式能解决上面的问题,那么也间接说明了Builder模式的优点。(1、区分必传和选传参数2、比较明确的表示出参数的含义)
    这里直接把书中代码搬过来:
     1 public class NutritionFacts {
     2     private final int servingSize;//份容量
     3     private final int servings;//
     4     private final int calories;//卡路里
     5     private final int fat;//脂肪
     6     private final int sodium;//
     7     private final int carbohydrate;//糖类
     8     public static class Builder{
     9         private final int servingSize;//份容量
    10         private final int servings;//11         //必要参数
    12         public Builder(int servingSize,int servings){
    13             this.servingSize=servingSize;
    14             this.servings=servings;
    15         }
    16         private  int calories = 0;
    17         private  int fat = 0;
    18         private  int sodium = 0;
    19         private  int carbohydrate = 0;
    20         public Builder calories(int val){
    21             calories = val;
    22             return this;
    23         }
    24         public Builder fat(int val){
    25             fat = val;
    26             return this;
    27         }
    28         public Builder sodium(int val){
    29             sodium = val;
    30             return this;
    31         }
    32         public Builder carbohydrate(int val){
    33             carbohydrate = val;
    34             return this;
    35         }
    36         public NutritionFacts build(){
    37             return new NutritionFacts(this);
    38         }
    39     }
    40     private NutritionFacts(Builder builder){
    41         servingSize = builder.servingSize;
    42         servings = builder.servings;
    43         calories = builder.calories;
    44         fat = builder.fat;
    45         sodium = builder.sodium;
    46         carbohydrate = builder.carbohydrate;
    47     }
    48 }
    使用时比较简单,而且比较清晰:
    1 NutritionFacts cocoCola = new NutritionFacts.Builder(20,15).calories(11).carbohydrate(12).build();
    注意点:
    1、对于NutritionFacts来说,包含了三部分,final成员变量,私有构造器、静态内部类。final成员变量保证了初始化安全性,即在构造器执行完成之前必须显示对成员变量初始化。这里是在构造函数中传入Builder来对其初始化。
    2、构造器为什么要声明为私有的?保证了外部创建实例时只能通过静态内部类的build()方法来实现。
    3、为什么是静态内部类?因为构造器是私有的,导致外部只能通过内部类的build()方法来实现。而非静态内部类对象的创建又依赖于外部类对象,即必须有外部类对象来创建(外部类对象.new InnerClassName()),这样就陷入了死循环。而静态内部类不需要依赖于外部类对象,只需要通过 “new OutClassName.InnerClassName()”就可以完成实例化。
    4、内部类Builder通过final关键字来区分必需参数和非必需参数。通过builder()方法完成外部类实例化。这里利用内部类可以访问外部私有元素的特性。
    5、总的来看,就是外部类通过静态内部类完成了自己成员变量的初始化。
     参考书籍:
    《Effective Java》
  • 相关阅读:
    AC日记——Little Elephant and Numbers codeforces 221b
    AC日记——Little Elephant and Function codeforces 221a
    AC日记——Mice and Holes codeforces 797f
    AC日记——Sliding Window poj 2823
    Poj 2976 Dropping tests(01分数规划 牛顿迭代)
    Bzoj 1968: [Ahoi2005]COMMON 约数研究
    洛谷 P2424 约数和
    Hdu Can you find it?(二分答案)
    SPOJ GSS1
    Bzoj 2243: [SDOI2011]染色(树链剖分+线段树)
  • 原文地址:https://www.cnblogs.com/uodut/p/7070424.html
Copyright © 2020-2023  润新知