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 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
http://programmers.stackexchange.com/questions/225207/why-should-a-builder-be-an-inner-class-instead-in-its-own-class-filehttps://softwareengineering.stackexchange.com/questions/225207/why-should-a-builder-be-an-inner-class-instead-in-its-own-class-filehttp://programmers.stackexchange.com/questions/118039/benefit-of-using-static-inner-builder-classhttps://softwareengineering.stackexchange.com/questions/118039/benefit-of-using-static-inner-builder-class
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(); }