Static factories and constructors both have one limitation: they do not extend well to a large number of optional arguments. Take a class that represents a nutrition label on a packaged food. The labels have some required fields, such as net amount, gross weight and calories per serving, and more than 20 optional fields, such as total fat, saturated fat, trans fat, cholesterol, sodium and more. Most products have only a few of these optional fields and have non-zero values.

What kind of constructor or static factory should you write for such a class? The traditional approach is to use a scalable constructor, in which only the required parameters are supplied to the constructor. That is, the first constructor is provided with a single optional argument, the second constructor with two optional arguments, and so on, and the last constructor with all optional arguments. This is what it looks like in practice. For brevity, show only the case where there are four optional fields:

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 NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; }}Copy the code

When you want to create an instance, you can use a constructor that contains the shortest argument list of the required arguments

NutritionFacts cocaCola =new NutritionFacts(240, 8, 100, 0, 35, 27);
Copy the code

Normally, this constructor contains many extra arguments, but you must pass a value for them. In this case, we pass a value of 0 for FAT. This may not seem so bad when there are only six parameters, but it can quickly spiral out of control as more parameters are added. In short, the scalable constructor pattern works, but client-side code is difficult to write and even harder to read when there are many parameters

When you encounter many optional arguments in constructors, another option is the JavaBean pattern, in which you call a no-argument constructor to create an object and then call setter methods to set each required argument and each optional argument of interest

public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // Required; no default value private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; }}Copy the code

This pattern does not have the disadvantages of the scalable constructor pattern. Creating an instance is easy, and while a bit verbose, it’s easy to read the generated code:

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
Copy the code

Another option is to combine the security of the scalable constructor pattern with the readability of the JavaBean pattern. It is a form of builder mode [Gamma95]. Instead of generating the desired object directly, the client calls the constructor (or static factory) with all the required parameters and gets a Builder object. The client then calls methods like setters on the builder object to set each optional parameter of interest.

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; }}Copy the code

The NutritionFacts class is immutable and all parameter defaults are in one place. Setter methods for the builder return the builder itself, which can then be called in chain form, resulting in a smooth API. Here’s what the client code looks like:

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