The essence of the enhanced builder pattern in Java

Josh Bloch first talked about some enhancements to the Builder Pattern back in 2006. Then Xavi and Mario took it a bit further with some nice clear examples.

I stumbled on these articles when I began looking more into how to make Java model objects that were both immutable, and yet still convenient. An immutable builder has come in handy for me in several situations - hence I thought I would pass on this simple and useful pattern too.

Here is an example of my version of the "enhanced immutable Builder" (also apparently called the "Essence" pattern in C/C#/etc circles).

package com.model.account;

import java.util.Date;
import com.model.AbstractBuilder;

public final class ImmutableAccount  {

   private final Date date;
   private double referenceValue;
   private double budgetValue;
   private double benchmarkValue;

   /**
    * Public Builder internal class that 
    * API users must use to construct the object - with this
    * approach can make an immutable object AND allow the use of
    * the convenient builder creational pattern.
    * 
    * NOTE - Builder constructor contains REQUIRED parameters. 
    * NOTE - OPTIONAL parameters are available in the builder sequence.
    * NOTE - The build() method returns the final instance of the object. 
    * 
    */
   public static class Builder extends AbstractBuilder {

      private ImmutableAccount account;

      /**
       * Internal Builder class. 
       * 
       * @param id
       * @param description
       * @param date
       */
      // REQUIRED fields in the Builder constructor
      public Builder(Date date) {
         this.account = new ImmutableAccount(date);
      }

      // OPTIONAL fields as Builder methods
      public Builder referenceValue(double val) {
         this.checkBuilt();
         this.account.referenceValue = val;
         return this;
      }
      public Builder budgetValue(double val) {
         this.checkBuilt();
         this.account.budgetValue = val;
         return this;
      }
      public Builder benchmarkValue(double val) {
         this.checkBuilt();
         this.account.benchmarkValue = val;
         return this;
      }

      // the BUILD method returns the instance
      public ImmutableAccount build() {
         this.checkBuilt();
         this.isBuilt = true;
         return this.account;
      }
   }

   /**
    * Main method object has a PRIVATE constructor intentionally - 
    * require that the Builder is used to create this object.
    * 
    * @param date
    */
   private ImmutableAccount(Date date) {
      this.date = date;
   }   

   //
   // accessors
   //
   public String getId() {
      return this.id;
   }
   public String getDescription() {
      return this.description;
   }
   public double getBudgetValue() {
      return this.budgetValue;
   }
   public double getBenchmarkValue() {
      return this.benchmarkValue;
   }
   public double getReferenceValue() {
      return this.referenceValue;
   }
   
   //
   // mutators that DO NOT mutate ;) - convenience that return new immutable object replacements
   // we make sure not to name or call these "set"ters so that it doesn't potentially confuse (especially with tools)
   //
   public ImmutableAccount updateDate(Date value) {
      return new ImmutableAccount.Builder(this.id, this.description, value).benchmarkValue(this.benchmarkValue)
               .budgetValue(this.budgetValue).referenceValue(this.referenceValue).build();
   }
   public ImmutableAccount updateBudgetValue(double value) {
      return new ImmutableAccount.Builder(this.id, this.description, this.date).benchmarkValue(this.benchmarkValue)
               .budgetValue(value).referenceValue(this.referenceValue).build();
   }
   public ImmutableAccount updateBenchmarkValue(double value) {
      return new ImmutableAccount.Builder(this.id, this.description, this.date).benchmarkValue(value)
               .budgetValue(this.budgetValue).referenceValue(this.referenceValue).build();
   }
   public ImmutableAccount updateReferenceValue(double value) {
      return new ImmutableAccount.Builder(this.id, this.description, this.date).benchmarkValue(this.benchmarkValue)
               .budgetValue(this.budgetValue).referenceValue(value).build();
   }   
}
And the AbstractBuilder the Builder extends is ultra simple too:
package com.model;

public abstract class AbstractBuilder {

   protected boolean isBuilt;

   public void checkBuilt() {
      if (this.isBuilt) {
         throw new IllegalStateException("The object cannot be modified after built");
      }
   }
}
Lastly, here is a test case that further demonstrates how you would actually use the immutable object with the Builder to create an instance:
package com.model.account;

import java.util.Date;
import junit.framework.Assert;
import com.axiomainc.test.AbstractTestCase;

public class ImmutableAccountTest extends AbstractTestCase {
   
   public void testBuilder() {
      
      ImmutableAccount account = new ImmutableAccount.Builder(new Date()).benchmarkValue(0.0).budgetValue(1000.0).referenceValue(10.0).build();
      System.out.println("account PRE - " + account);      
      
      account = account.updateBenchmarkValue(2000.0);
      System.out.println("account POST - " + account);
      
      Assert.assertEquals(2000.0, account.getBenchmarkValue());      
   }
}