Monthly Archives: February 2014

The Blind Builder: An alternative design pattern to the Bloch Builder, for highly-extendable classes

This post is presented in a question-answer format, as was posted on programmers.stackexchange.com

The question

I have been greatly influenced by Joshua Bloch’s Effective Java book (2nd edition), probably more so than with any programming book I’ve read. In particular, his Builder Pattern (item 2) has had the greatest effect.

Despite Bloch’s builder getting me farther in the past couple of months than in my past ten years of programming, I am still finding myself hitting the same wall: Extending classes with self-returning method-chains is at best discouraging, and at worst a nightmare–especially when generics come into play, and especially with self-referential generics (such as Comparable<T extends Comparable>).

There are two big problems, only the second of which I’d like to focus on in this question:


  1. The first is “how to share self-returning method chains, without having to re-implement them in every…single…class?” For those who may be curious, I’ve addressed this part at the bottom of this question-post, but it’s not what I want to focus on here.

  2. The second, which I am asking for comment on, is “how can I implement a builder in classes that are themselves intended to be extended by many other classes?” Extending a class with a builder is naturally more difficult than extending one without. Extending a class that has a builder that also has generics associated to it, is unwieldy.

So that is my question: How can I improve upon the Bloch Builder, so I can feel free to attach a builder to any class–even when that class is meant to be a “base class” that may be extended and sub-extended many times over–without discouraging my future-self, or users of my library, because of the extra baggage the builder (and its potential generics) impose on them?

Thank you.


***Addendum*** My question focuses on part 2 above, but I want to elaborate a bit on problem one, including how I’ve dealt with it:


  1. The first problem is “how to share self-returning method chains, without having to re-implement them in every…single…class?” This is not to prevent extending classes from having to re-implement these chains, which, of course, they must–rather, how to prevent non-sub-classes, that want to take advantage of these method chains, from having to re-implement every self-returning function in order for their users to be able to take advantage of them? For this I’ve come up with a needer-needable design that I’ll print the interface skeletons for here, and leave it at that for now. It has worked well for me (this design was years in the making…the hardest part was avoiding circular dependencies):

public interface Chainable  {
   Chainable chainID(boolean b_setStatic, Object o_id);
   Object getChainID();
   Object getStaticChainID();
}
public interface Needable<O,R extends Needer> extends Chainable  {
   boolean isAvailableToNeeder();
   Needable<O,R> startConfigReturnNeedable(R n_eeder);
   R getActiveNeeder();
   boolean isNeededUsable();
   R endCfg();
}
public interface Needer  {
   void startConfig(Class<?> cls_needed);
   boolean isConfigActive();
   Class getNeededType();
   void neeadableSetsNeeded(Object o_fullyConfigured);
}


I have created what, for me, is a big improvement over Josh Bloch’s Builder Pattern. Not to say in any way that it is “better”, just that in *my personal development environment*, it does provide me some advantages–the biggest being that it decouples the builder from its to-be-built class.

I have thoroughly documented this alternative below, which I call the Blind Builder Pattern.

(Maybe not "the" answer, but) My answer

As an alternative to Joshua Bloch’s Builder Pattern (item 2 in Effective Java, 2nd edition), I have created what I call the “Blind Builder Pattern”. The Blind Builder shares many of the benefits of Bloch’s Builder, and aside from a single character, is used in exactly the same way. Blind Builders have the advantage of


  • decoupling the builder from its enclosing class, eliminating a circular dependency,

  • greatly reduces the size of the source code of (what is no longer) the enclosing class, and

  • allows the ToBeBuilt class to be extended without having to extend its builder.

In this documentation, I’ll refer to the class-being-built as the “ToBeBuilt” class.

A class implemented with a Bloch Builder

A Bloch Builder is a public static class contained inside the class that it builds. An example:

public class UserConfig  {
   private final String sName    ;
   private final int    iAge     ;
   private final String sFavColor;

   public UserConfig(UserConfig.Cfg uc_c)  {
      //transfer
         try  {
            sName  = uc_c.sName;
         }  catch(NullPointerException rx)  {
            throw  new NullPointerException("uc_c");
         }
         iAge      = uc_c.iAge;
         sFavColor = uc_c.sFavColor;

      //VALIDATE ALL FIELDS HERE

   }
   public String toString()  {
      return  "name=" + sName + ", age=" + iAge + ", sFavColor=" + sFavColor;
   }

   public static class Cfg  {
      private String sName    ;
      private int    iAge     ;
      private String sFavColor;
      public Cfg(String s_name)  {
         sName = s_name;
      }
      //self-returning setters...START
         public Cfg age(int i_age)  {
            iAge = i_age;
            return  this;
         }
         public Cfg favoriteColor(String s_color)  {
            sFavColor = s_color;
            return  this;
         }
      //self-returning setters...END
      public UserConfig build()  {
         return  (new UserConfig(this));
      }
   }
}

Instantiating a class with a Bloch Builder

UserConfig uc = new UserConfig.Cfg("Kermit").
	age(50).favoriteColor("green").
	build();

The same class, implemented as a Blind Builder

There are three parts to a Blind Builder, each of which is in a separate source-code file:


  1. The ToBeBuilt class (in this example: UserConfig)

  2. Its “Fieldable” interface

  3. The builder

1. The to-be-built class

The to-be-built class accepts its Fieldable interface as its only constructor parameter. The constructor sets all internal fields from it, and validates each. Most importantly, this ToBeBuilt class has no knowledge of its builder.

public class UserConfig  {
   private final String sName    ;
   private final int    iAge     ;
   private final String sFavColor;

   public UserConfig(UserConfig_Fieldable uc_f)  {
      //transfer
         try  {
            sName  = uc_f.getName();
         }  catch(NullPointerException rx)  {
            throw  new NullPointerException("uc_f");
         }
         iAge      = uc_f.getAge();
         sFavColor = uc_f.getFavoriteColor();

      //VALIDATE ALL FIELDS HERE

   }
   public String toString()  {
      return  "name=" + sName + ", age=" + iAge + ", sFavColor=" + sFavColor;
   }
}

As noted by one smart commenter on programmers.stackexchange (who inexplicably deleted their answer), if the ToBeBuilt class also implements its Fieldable, its one-and-only constructor can be used as both its primary and copy constructor (a disadvantage is that fields are always validated, even though it is known that the fields in the original `ToBeBuilt` are valid).

2. The “Fieldable” interface

The fieldable interface is the “bridge” between the ToBeBuilt class and its builder, defining all fields necessary to build the object. This interface is required by the ToBeBuilt classes constructor, and is implemented by the builder. Since this interface may be implemented by classes other than the builder, any class may easily instantiate the ToBeBuilt class, without being forced to use its builder. This also makes it easier to extend the ToBeBuilt class, when extending its builder is not desirable or necessary.

As described in a below section, I do not document the functions in this interface at all.

public interface UserConfig_Fieldable  {
   String getName();
   int getAge();
   String getFavoriteColor();
}

3. The builder

The builder implements the Fieldable class. It does no validation at all, and to emphasize this fact, all of its fields are public and mutable. While this public accessibility is not a requirement, I prefer and recommend it, because it re-enforces the fact that validation does not occur until the ToBeBuilt‘s constructor is called. This is important, because it is possible for another thread to manipulate the builder further, before it is passed to the ToBeBuilt‘s constructor. The only way to guarantee the fields are valid–assuming the builder cannot somehow "lock" its state–is for the ToBeBuilt class to do the final check.

Finally, as with the Fieldable interface, I do not document any of its getters.

public class UserConfig_Cfg implements UserConfig_Fieldable  {
   public String sName    ;
   public int    iAge     ;
   public String sFavColor;

   public UserConfig_Cfg(String s_name)  {
      sName = s_name;
   }

   //self-returning setters...START
      public UserConfig_Cfg age(int i_age)  {
         iAge = i_age;
         return  this;
      }
      public UserConfig_Cfg favoriteColor(String s_color)  {
         sFavColor = s_color;
         return  this;
      }
   //self-returning setters...END

   //getters...START
      public String getName()  {
         return  sName;
      }
      public int getAge()  {
         return  iAge;
      }
      public String getFavoriteColor()  {
         return  sFavColor;
      }
   //getters...END

   public UserConfig build()  {
      return  (new UserConfig(this));
   }
}

Instantiating a class with a Blind Builder

UserConfig uc = new UserConfig_Cfg("Kermit").
	age(50).favoriteColor("green").
	build();

The only difference is “UserConfig_Cfg” instead of “UserConfig.Cfg

Other notes

Disadvantages:


  • Blind Builders cannot access private members of its ToBeBuilt class,

  • They are more verbose, as getters are now required in both the builder and in the interface, and each class is now split into three pieces.

  • Everything for a single class is no longer in just one place.

Compiling a Blind Builder is straight-forward:


  1. ToBeBuilt_Fieldable

  2. ToBeBuilt

  3. ToBeBuilt_Cfg


The Fieldable interface is entirely optional

For a ToBeBuilt class with few required fields–such as this UserConfig example class, the constructor could simply be

public UserConfig(String s_name, int i_age, String s_favColor)  {

And called in the builder with

public UserConfig build()  {
   return  (new UserConfig(getName(), getAge(), getFavoriteColor()));
}

Or even by eliminating the getters (in the builder) altogether:

   return  (new UserConfig(sName, iAge, sFavoriteColor));

By passing fields directly, the ToBeBuilt class is just as "blind" (unaware of its builder) as it is with the Fieldable interface. However, for ToBeBuilt classes which and are intended to be "extended and sub-extended many times" (which is in the title of this post), any changes to any field necessitates changes in every sub-class, in every builder and ToBeBuilt constructor. As the number of fields and sub-classes increases, this becomes impractical to maintain.

(Indeed, with few necessary fields, using a builder at all may be overkill. For those interested, here is a sampling of some of the larger Fieldable interfaces in my personal library.)

Secondary classes in sub-package

I choose to have all builder and the Fieldable classes, for all Blind Builders, in a sub-package of their ToBeBuilt class. The sub-package is always named "z". This prevents these secondary classes from cluttering up the JavaDoc package list. For example


  • library.class.my.UserConfig

  • library.class.my.z.UserConfig_Fieldable

  • library.class.my.z.UserConfig_Cfg

As mentioned above, all validation occurs in the ToBeBuilt‘s constructor. Here is the constructor again with example validation code:

public UserConfig(UserConfig_Fieldable uc_f)  {
   //transfer
      try  {
         sName  = uc_f.getName();
      }  catch(NullPointerException rx)  {
         throw  new NullPointerException("uc_f");
      }
      iAge      = uc_f.getAge();
      sFavColor = uc_f.getFavoriteColor();

   //validate (should really pre-compile the patterns...)
      try  {
         if(!Pattern.compile("\\w+").matcher(sName).matches())  {
            throw  new IllegalArgumentException("uc_f.getName() (\"" + sName + "\") may not be empty, and must contain only letters digits and underscores.");
         }
      }  catch(NullPointerException rx)  {
         throw  new NullPointerException("uc_f.getName()");
      }
      if(iAge < 0)  {
         throw  new IllegalArgumentException("uc_f.getAge() (" + iAge + ") is less than zero.");
      }
      try  {
         if(!Pattern.compile("(?:red|blue|green|hot pink)").matcher(sFavColor).matches())  {
            throw  new IllegalArgumentException("uc_f.getFavoriteColor() (\"" + uc_f.getFavoriteColor() + "\") is not red, blue, green, or hot pink.");
         }
      }  catch(NullPointerException rx)  {
         throw  new NullPointerException("uc_f.getFavoriteColor()");
      }
}

Documenting Builders

This section is applicable to both Bloch Builders and Blind Builders. It demonstrates how I document the classes in this design, making setters (in the builder) and their getters (in the ToBeBuilt class) directly cross-referenced to each other–with a single mouse-click, and without the user needing to know where those functions actually reside–and without the developer having to document anything redundantly.

Getters: In the ToBeBuilt classes only

Getters are documented only in the ToBeBuilt class. The equivalent getters both in the *_Fieldable and *_Cfg classes are ignored. I don’t document them at all.

/**
   <P>The user's age.</P>
   @return  An int representing the user's age.
   @see  UserConfig_Cfg#age(int)
   @see  getName()
 **/
public int getAge()  {
   return  iAge;
}

The first @see is a link to its setter, which is in the builder class.

Setters: In the builder-class

The setter is documented as if it is in the ToBeBuilt class, and also as if it does the validation (which really is done by the ToBeBuilt‘s constructor). The asterisk (“*“) is a visual clue indicating that the link’s target is in another class.

/**
   <P>Set the user's age.</P>
   @param  i_age  May not be less than zero. Get with {@code UserConfig#getName() getName()}*.
   @see  #favoriteColor(String)
 **/
public UserConfig_Cfg age(int i_age)  {
   iAge = i_age;
   return  this;
}

Further information

Putting it all together: The full source of the Blind Builder example, with complete documentation

UserConfig.java

import  java.util.regex.Pattern;
   /**
      <P>Information about a user -- <I>[builder: UserConfig_Cfg]</I></P>
      <P>Validation of all fields occurs in this classes constructor. However, each validation requirement is document only in the builder's setter functions.</P>
      <P>{@code java xbn.z.xmpl.lang.builder.finalv.UserConfig}</P>
    **/
   public class UserConfig  {
      public static final void main(String[] igno_red)  {
         UserConfig uc = new UserConfig_Cfg("Kermit").age(50).favoriteColor("green").build();
         System.out.println(uc);
      }
      private final String sName    ;
      private final int    iAge     ;
      private final String sFavColor;
      /**
         <P>Create a new instance. This sets and validates all fields.</P>

         @param  uc_f  May not be {@code null}.
       **/
      public UserConfig(UserConfig_Fieldable uc_f)  {
         //transfer
            try  {
               sName  = uc_f.getName();
            }  catch(NullPointerException rx)  {
               throw  new NullPointerException("uc_f");
            }
            iAge      = uc_f.getAge();
            sFavColor = uc_f.getFavoriteColor();
         //validate
            try  {
               if(!Pattern.compile("\\w+").matcher(sName).matches())  {
                  throw  new IllegalArgumentException("uc_f.getName() (\"" + sName + "\") may not be empty, and must contain only letters digits and underscores.");
               }
            }  catch(NullPointerException rx)  {
               throw  new NullPointerException("uc_f.getName()");
            }
            if(iAge < 0)  {
               throw  new IllegalArgumentException("uc_f.getAge() (" + iAge + ") is less than zero.");
            }
            try  {
               if(!Pattern.compile("(?:red|blue|green|hot pink)").matcher(sFavColor).matches())  {
                  throw  new IllegalArgumentException("uc_f.getFavoriteColor() (\"" + uc_f.getFavoriteColor() + "\") is not red, blue, green, or hot pink.");
               }
            }  catch(NullPointerException rx)  {
               throw  new NullPointerException("uc_f.getFavoriteColor()");
            }
      }
      //getters...START
         /**
            <P>The user's name.</P>

            @return  A non-{@code null}, non-empty string.
            @see  UserConfig_Cfg#UserConfig_Cfg(String)
            @see  #getAge()
            @see  #getFavoriteColor()
          **/
         public String getName()  {
            return  sName;
         }
         /**
            <P>The user's age.</P>

            @return  A number greater-than-or-equal-to zero.
            @see  UserConfig_Cfg#age(int)
            @see  #getName()
          **/
         public int getAge()  {
            return  iAge;
         }
         /**
            <P>The user's favorite color.</P>

            @return  A non-{@code null}, non-empty string.
            @see  UserConfig_Cfg#age(int)
            @see  #getName()
          **/
         public String getFavoriteColor()  {
            return  sFavColor;
         }
      //getters...END
      public String toString()  {
         return  "getName()=" + getName() + ", getAge()=" + getAge() + ", getFavoriteColor()=" + getFavoriteColor();
      }
   }

UserConfig_Fieldable.java

 /**
      <P>Required by the {@link UserConfig} {@code UserConfig#UserConfig(UserConfig_Fieldable) constructor}.</P>
    **/
   public interface UserConfig_Fieldable  {
      String getName();
      int getAge();
      String getFavoriteColor();
   }

UserConfig_Cfg.java

import  java.util.regex.Pattern;
   /**
      <P>Builder for {@link UserConfig}.</P>
      <P>Validation of all fields occurs in the <CODE>UserConfig</CODE> constructor. However, each validation requirement is document only in this classes setter functions.</P>
    **/
   public class UserConfig_Cfg implements UserConfig_Fieldable  {
      public String sName    ;
      public int    iAge     ;
      public String sFavColor;
      /**
         <P>Create a new instance with the user's name.</P>

         @param  s_name  May not be {@code null} or empty, and must contain only letters, digits, and underscores. Get with {@code UserConfig#getName() getName()}{@code ()}*.
       **/
      public UserConfig_Cfg(String s_name)  {
         sName = s_name;
      }
      //self-returning setters...START
         /**
            <P>Set the user's age.</P>

            @param  i_age  May not be less than zero. Get with {@code UserConfig#getName() getName()}{@code ()}*.
            @see  #favoriteColor(String)
          **/
         public UserConfig_Cfg age(int i_age)  {
            iAge = i_age;
            return  this;
         }
         /**
            <P>Set the user's favorite color.</P>

            @param  s_color  Must be {@code "red"}, {@code "blue"}, {@code green}, or {@code "hot pink"}. Get with {@code UserConfig#getName() getName()}{@code ()}*.
            @see  #age(int)
          **/
         public UserConfig_Cfg favoriteColor(String s_color)  {
            sFavColor = s_color;
            return  this;
         }
      //self-returning setters...END
      //getters...START
         public String getName()  {
            return  sName;
         }
         public int getAge()  {
            return  iAge;
         }
         public String getFavoriteColor()  {
            return  sFavColor;
         }
      //getters...END
      /**
         <P>Build the UserConfig, as configured.</P>

         @return  <CODE>(new {@link UserConfig#UserConfig(UserConfig_Fieldable) UserConfig}(this))</CODE>
       **/
      public UserConfig build()  {
         return  (new UserConfig(this));
      }
   }
Advertisements