本文参考

本篇文章参考自《Effective Java》第三版第二条"Consider a builder when faced with many constructor parameters"

静态工厂方法和构造器的不足之处

当一个类中有大量的字段时,尽管能通过设置不同的形参列表和方法名进行重载或使用静态工厂方法进行一定的简化,但我们还是会难以避免地为方法签名设置了较长的形参列表并直接使用到这些方法,以致于"it is hard to write client code when there are many parameters, and harder still to read it",原文将这种颇为不优美的解决方案称为"telescoping constructor pattern"(重叠构造器模式)

另一种不优美的解决方案是"JavaBean pattern"(JavaBean模式),我们需要频繁地调用set方法来构造我们需要的实例,只要有一个set方法没有调用,那么构造出来的实例就是不完整的,而且JavaBean模式使得把类做成不可变的可能性不复存在

a JavaBean may be in an inconsistent state partway through its construction and the JavaBeans pattern precludes the possibility of making a class immutable

Builder pattern(建造者模式)示例

我们不直接创建我们需要的实例,首先仅设置我们必需的字段值,返回一个Builder 实例

然后,上一步返回的Builder 实例调用类似set的方法来设置其它可选的字段值,同样,返回的都是Builder 实例

最后,Builder实例调用无参的build()方法,生成我们真正需要的实例,这种设计模式实现了类的不可变性

Instead of making the desired object directly, the client calls a constructor (or static factory) with all of the required parameters and gets a builder object.

Then the client calls setter-like methods on the builder object to set each optional parameter of interest.

Finally, the client calls a parameterless build method to generate the object, which is typically immutable.

The builder is typically a static member class of the class it builds.

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 parameters - initialized to default values

    private int
calories = 0;

    private int fat = 0;

    private int sodium = 0;

    private int carbohydrate = 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 sodium(int val) {

      sodium = val;

      return this;
    }

    public Builder carbohydrate(int val) {

      carbohydrate = 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;
  }

  public static void main(String[] args) {

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

NutritionFacts类的字段在Builder静态内部类中有一份一模一样的声明,并且默认参数值都在Builder类中,NutritionFacts类本身不具备任何为字段"直接"赋值的构造方法,而是由Builder实例"间接"完成

注意Builder的所有"set"方法都返回Builder的实例,使得我们可以链式调用其它"set"方法,原文将这种特性称为"fluent API",我们还可以根据具体情况判断是否需要为Builder的方法检查传入参数的有效性

The NutritionFacts class is immutable, and all parameter default values are in one place. The builder's setter methods return the builder itself so that invocations can be chained, resulting in a fluent API

The Builder pattern is well suited to class hierarchies

Builder类也适用于类层次结构,抽象类有抽象的 builder,具体类有具体的 builder,下面是原文抽象类的示例

abstract class Pizza {

  public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}

  final Set<Topping> toppings;

  // recursive type parameter
  abstract static class Builder<T extends Builder<T>> {

    EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);

    public T addTopping(Topping topping) {

      toppings.add(Objects.requireNonNull(topping));

      return self();
    }

    abstract Pizza build();

    // Subclasses must override this method to return "this"

    protected abstract
T self();
  }

  Pizza(Builder<?> builder) {

    toppings = builder.toppings.clone();
  }
}

下面是两个具体的实现类

class NyPizza extends Pizza {

  public enum Size {SMALL, MEDIUM, LARGE}

  private final Size size;

  public static class Builder extends Pizza.Builder<Builder> {

    private final Size size;

    public Builder(Size size) {

      this.size = Objects.requireNonNull(size);
    }

    @Override

    public NyPizza build() {

      return new NyPizza(this);
    }

    @Override

    protected Builder self() {

      return this;
    }
  }

  private NyPizza(Builder builder) {

    super(builder);

    size = builder.size;
  }
}

class Calzone extends Pizza {

  private final boolean sauceInside;

  public static class Builder extends Pizza.Builder<Builder> {

    private boolean sauceInside = false; // Default

    public
Builder sauceInside() {

      sauceInside = true;

      return this;
    }

    @Override

    public Calzone build() {

      return new Calzone(this);
    }

    @Override

    protected Builder self() {

      return this;
    }
  }

  private Calzone(Builder builder) {

    super(builder);

    sauceInside = builder.sauceInside;
  }
}

注意在子类中,build()方法的返回类型是子类本身,而不是抽象类,否则就需要我们在调用build()方法后,将返回的抽象类进行强制类型转换

Effective Java —— 多字段下考虑使用建造者模式构建实例的更多相关文章

  1. java的23种设计模式之建造者模式

    场景和本质 场景 本质 案例 原理 应用场景 场景和本质 场景 我们要建造一个复杂的产品.比如:神州飞船,Iphone.这个复杂的产品的创建.有这样一个问题需要处理:装配这些子组件是不是有个步骤问题? ...

  2. Java设计模式(四)Builder建造者模式

    一.场景描述 建造者模式同工厂模式.抽象工厂模式一样,用于创建继承类对象. 工厂模式:http://www.cnblogs.com/mahongbiao/p/8618970.html 抽象工厂模式:h ...

  3. JAVA设计模式之简单粗暴学建造者模式

    文章由浅入深,先用简单例子说明建造者,然后分析模式的优缺点,最后结合优秀开源框架Mybatis,说明该模式的用处. 1.先定义一个机器人模型 package com.jstao.model; publ ...

  4. java设计模式(二)单例模式 建造者模式

    (三)单例模式 单例模式应该是最常见的设计模式,作用是保证在JVM中,该对象仅仅有一个实例存在. 长处:1.降低某些创建比較频繁的或者比較大型的对象的系统开销. 2.省去了new操作符,减少系统内存使 ...

  5. Java学习笔记——设计模式之九.建造者模式

     建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. Product类: package cn.happy.design_pattern._09b ...

  6. Java设计模式菜鸟系列(十五)建造者模式建模与实现

    转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39863125 建造者模式(Builder):工厂类模式提供的是创建单个类的模式.而建造者模 ...

  7. Java设计模式之(三)——建造者模式

    1.什么是建造者模式 Separate the construction of a complex object from its representation so that the same co ...

  8. java 建造者模式

    package de.bvb.test3; /** * 建造者模式: 假如一个类有4个字段,每个字段或者每几个字段的组合都需要设置为构造函数,构造函数就比较麻烦 * 而且如果再加一个字段进去也不好拓展 ...

  9. Java设计模式之建造者模式(Builder Pattern)

    前言 这篇文章主要向大家讲解什么是建造者模式,建造者模式的实例讲解及应用场景等知识点. 一.建造者介绍 ​ 用户可以不知道产品的构建细节直接可以创建复杂的对象,主要是分离了产品的构建和装配,这样就实现 ...

随机推荐

  1. 如何处理大体积 XLSX/CSV/TXT 文件?

    在开发过程中,可能会遇到这样的需求,我们需要从本地的 Excel 或 CSV 等文件中解析出信息,这些信息可能是考勤打卡记录,可能是日历信息,也可能是近期账单流水.但是它们共同的特点是数据多且繁杂,人 ...

  2. git问题:gpg failed to sign the data fatal: failed to write commit object问题

    今天用版本控制工具git提交时一直出现的问题:gpg  failed to sign the data fatal: failed to write commit object, gpg是一个加密软件 ...

  3. /etc/fstab文件的详解

    转至:https://blog.csdn.net/youmatterhsp/article/details/83933158 一./etc/fstab文件的作用 磁盘被手动挂载之后都必须把挂载信息写入 ...

  4. drawable如何修改图片大小

    这个问题刚开始遇到是导入图片太大,在网上找了许多教程大多都是采用setBounds()方法自己尝试许多次还是没成功,在经历了多达数个小时折磨后我找到两个方法1.在导入图片之前直接对图片进行修改大小.( ...

  5. 什么是NFT?

    我有一个年轻朋友,最近买了一个数字艺术品,9百多入手,几周后卖掉,赚了7万多,他告诉我这个东西叫NFT. 2021年twitter创始人杰克.多西将自己发布的第一条twitter通过NFT以250万美 ...

  6. Redis原理再学习05:数据结构-整数集合intset

    intset介绍 intset 整数集合,当一个集合只有整数元素,且元素数量不多时,Redis 就会用整数集合作为集合键的底层实现. redis> SADD numbers 1 3 5 7 9 ...

  7. vue轻量进度条

    **### vue------ mode 好玩东西+1: 轻量级进度条: 1.引入 import NProgress from 'nprogress'; // progress bar import ...

  8. mysql更改my.ini配置文件以后mysql服务无法启动

    最近在调试mysql时,更改了mysql的端口以后发现,mysql怎么改都启动不了,从其它机器重新复制一个my.ini文件就可以启动,这是由于一般用记事本打开配置文件同时更改的ini的格式,我们需要重 ...

  9. SoftwareTeacher直播自我感想

    今天老师发布了一个链接直播是关于:同学们聊聊学习软件工程,CS 课程的问题下面是我的个人感悟和笔记 一.编程技术的提升 编程并不是一件很难的事情,就如开车一样,只有多加练习,自己的技术才能提升上去.拿 ...

  10. VuePress 博客之 SEO 优化(三)标题、链接优化

    前言 在 <一篇带你用 VuePress + Github Pages 搭建博客>中,我们使用 VuePress 搭建了一个博客,最终的效果查看:TypeScript 中文文档. 本篇讲讲 ...