5

I use Java builder pattern introduced by Joshua Bloch. Sometimes, I find certain fields which is more expensive to be initialized with a default value compared to primitive types.

Hence, what my strategy is that.

  1. I delay the default value initialization operation for those fields.
  2. During build, I will only initialize them to default value, if they are not set by caller before.

I am not sure whether it is good to do so? Is there any catch may happen? Like, thread safety issues? So far, I do not see any issue with this.

package sandbox;

import java.util.Calendar;

/**
 *
 * @author yccheok
 */
// 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;

    private final java.util.Calendar calendar;    // !!!

    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 carbohydrate = 0;
        private int sodium = 0;

        // We suppose to provide a default value for calendar. However, it may
        // seem expensive. We will do it later during build.        
        private java.util.Calendar calendar = null;

        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() {
            // !!!
            if (this.calendar == null) {
                this.calendar = Calendar.getInstance();
            }            
            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;
        calendar = builder.calendar;
    }
}
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

3 Answers3

4

what if I really want the calendar to be null? Your pattern won't work then. Otherwise, it seems OK for me.

nanda
  • 24,458
  • 13
  • 71
  • 90
  • 1
    OK. This remind me. Perhaps I should set a boolean flag to be dirty when user is setting calendar explicitly. Then during build, I will check for the boolean flag? – Cheok Yan Cheng Sep 15 '10 at 14:37
3

I don't see an issue with it. The Builder pattern is really an API that helps with certain tasks. You can do whatever you want under the covers of the API as long as you adhere to the principles of the pattern/API. I think lazy loading of expensive resources is fine, just make sure you document any behavior you think developers should know about.

NG.
  • 22,560
  • 5
  • 55
  • 61
2

Be careful, as Calendar is mutable, so you should defensive copy it in NutritionFacts constructor.

hhbarriuso
  • 976
  • 5
  • 7