构建复杂对象

本文定位于理解和总结<Effective Java>的所讲内容,而不是翻译,因此不当之处,还请广大网友指出。

复杂对象在这里指的是有三个以上(不包括三个)的属性的对象。当一个类具有很多属性时,使用构造器或者静态工厂的方式创建新对象时不可避免的需要对很多属性进行赋值,可维护性与可读性都很低。

通常使用重叠构造器(telescoping constructor)或者JavaBean模式来构建新对象(两种模式具体代码实例可参见<Effective Java>),可以提高代码可维护性和可读性,然而这两种方法并不完美。

重叠构造器模式

重叠构造器模式是指类有一系列的构造器,构造器的入参从仅包含必选参数依次递增到包含所有参数,且必选参数构造器通过逐级调用后续构造器来实现。

实际上,重叠构造器模式并没有解决任何问题,当可变参数增多时,客户端代码依然难写又难读,并且扩展性极差,每增加一个属性都要修改很多级的构造器。

JavaBean模式

JavaBean模式是指先通过无参的构造器来创建对象,然后通过setter填充属性的方法来构建新对象。

相对于重叠构造器模式来说,JavaBean模式可读性,可维护性,可扩展性都很高,但是JavaBean模式本身存在严重的缺陷。

  • 对象在构建过程中存在不一致性的可能
    由于对象的构建分多次调用才能完成,在构建完成之前,使用对象可能会引起运行时错误,编译期无法察觉。

  • 对象是可变的
    由于使用setter来构建对象,导致对象本身就失去了成为不可变对象的可能。

  • 需要依赖程序员来保证线程安全
    不一致性导致需要程序员额外的工作来保证线程安全。

构建者模式

除了以上两种方式以外,还可以使用构建者模式来创建对象,构建者模式通过吸收必选参数和链式配置可选参数来完成对象的构建。一般情况下,构建者模式可实现为构建对象的内部类。如下:

// Builder Pattern
public class NutritionFacts {
  private final int servingSize;
  private final int servings;
  private final int calories;
  private final int fat;
  private final int sodium;
  private final int carbohydrate;
  public static class Builder {
    // Required parameters
    private final int servingSize;
    private final int servings;
    // Optional
    private int calories = 0;
    private int fat = 0;
    private int carbohydrate = 0;
    private int sodium = 0;

    public Builder(int servingSize, int servings) {
      this.servingSize = servingSize;
      this.servings = servings;
    }
    public Builder calories(int val){ 
      calories = val;
      return this;
    }
    public Builder fat(int val){
      fat = val;
      return this;
    }
    public Builder carbohydrate(int val){
      carbohydrate = val; 
      return this;
    }
    public Builder sodium(int val){
      sodium = val;
      return this;
    }
    public NutritionFacts build() {
      return new NutritionFacts(this);
    }
  }
  private NutritionFacts(Builder builder) {
    servingSize = builder.servingSize;
    servings = builder.servings;
    calories = builder.calories;
    fat = builder.fat;
    sodium = builder.sodium;
    carbohydrate = builder.carbohydrate;
  }
}

在创建对象之前,需要先创建该对象构建者的对象,并提供必选参数,随后链式配置可选参数,调用构建方法来完成对象的构建,如下:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

构建者模式融合了以上两种方式的优点,同时也避免了缺点,既保证了可读性和可维护性又提高了线程安全。

因此当遇到有三个以上属性的复杂对象构建,尤其是有一些属性是可选属性时,不妨考虑使用构建者模式。

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

推荐阅读更多精彩内容