Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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 much farther in the 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<T>>).

There are two primary needs that I have, only the second of which I'd like to focus on in this question:

  1. The first problem 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 answer-post, but it's not what I want to focus on here.

  2. The second problem, 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 implements Needable, and therefore has significant generics associated to it, is unwieldy.

So that is my question: How can I improve upon (what I call) 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?


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

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);
}
share|improve this question

2 Answers 2

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 a very specific situation, it does provide 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.


Design Pattern: Blind Builder

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", which shares many of the benefits of the Bloch 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)  {      //CONSTRUCTOR
      //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;
   }
   //builder...START
   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));
      }
   }
   //builder...END
}

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)  {      //CONSTRUCTOR
      //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 (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"

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.
  • 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

Validation example

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));
   }
}

share|improve this answer
    
Definitely, it's an improvement. The Bloch's Builder, as implemented here, couples two concrete classes, these being the to-be-built one and its builder. This is bad design per se. The Blind Builder you describe breaks that coupling by having the to-be-built class define its construction dependency as an abstraction, which other classes can implement in a decoupled fashion. You've greatly applied what's an essential object-oriented design guideline. –  rucamzu Feb 14 at 7:37
3  
You should really blog about this somewhere if you haven't already, nice piece of algorithm design! I'm off sharing it now :-). –  Martijn Verburg Feb 14 at 10:25
2  
Thank you for the kind words. This is now the first post on my new blog: aliteralmind.wordpress.com/2014/02/14/blind_builder –  aliteralmind Feb 14 at 16:57
    
If the builder and built objects both implement Fieldable, the pattern starts to resemble one I've referred as the ReadableFoo/MutableFoo/ImmutableFoo, though rather than having the method to make a mutable thing be the "build" member of the builder, I call it asImmutable and include it in the ReadableFoo interface [using that philosophy, calling build on an immutable object would simply return a reference to the same object]. –  supercat Feb 14 at 21:35

I think the question here assumes something from the outset without attempting to prove it, that the builder pattern is inherently good.

tl;dr I think the builder pattern is rarely if ever a good idea.


Builder pattern purpose

The purpose of the builder pattern is to maintain two rules that will make consuming your class easier:

  1. Objects should not be able to be constructed in inconsistant/unusable/invalid states.

    • This refers to scenarios where for instance a Person object may be constructed without having it's Id filled in, while all pieces of code that use that object may require the Id just to properly work with the Person.
  2. Object constructors should not require too many parameters.

So the purpose of the builder pattern is non-controversially good. I think much of the desire and usage of it is based on analysis that has gone basically this far: We want these two rules, this gives these two rules - though I think it's worth investigating other ways to accomplish those two rules.


Why bother looking at other approaches?

I think the reason is well shown by the fact of this question itself; there is complexity and a lot of ceremony added to structures in applying the builder pattern to them. This question is asking how to resolve some of that complexity because as complexity oft does, it makes for a scenario that behaves strangely (inheriting). This complexity also increases maintenance overhead (adding, changing, or removing properties is far more complex than otherwise).


Other approaches

So for rule number one above, what approaches are there? The key this rule is referring to is that upon construction, an object has all the information it needs to function properly - and after construction that information cannot be changed externally (so it's immutable information).

One way to give all necessary information to an object at construction is simply add parameters to the constructor. If that information is demanded by the constructor, you won't be able to construct this object without all that information, therefore it will be constructed into a valid state. But what if the object requires a lot of information to be valid? Oh dang, if that's the case this approach would break rule #2 above.

Ok what else is there? Well you could simply take all that information that's necessary for your object to be in a consistent state, and bundle it up into another object which is taken at construction time. Your code above instead of having a builder pattern would then be:

//DTO...START
public class Cfg  {
   public String sName    ;
   public int    iAge     ;
   public String sFavColor;
}
//DTO...END

public class UserConfig  {
   private final String sName    ;
   private final int    iAge     ;
   private final String sFavColor;
   public UserConfig(Cfg uc_c)  {
      ...
   }

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

This isn't a great deal different from the builder pattern, though it is slightly simpler, and most importantly we're satisfying rule #1 and rule #2 now.

So why not go the bit extra and make it a full on builder? It's simply unnecessary. I satisfied both purposes of the builder pattern in this approach, with something slightly simpler, easier to maintain, and reusable. That last bit is key, this example being used is imaginary and doesn't lend itself to real-world semantic purpose, so let's show how this approach results in a reusable DTO rather than a single purpose class.

public class NetworkAddress {
   public String Ip;
   public int Port;
   public NetworkAddress Proxy;
}

public class SocketConnection {
   public SocketConnection(NetworkAddress address) {
      ...
   }
}

public class FtpClient {
   public FtpClient(NetworkAddress address) {
      ...
   }
}

So when you build cohesive DTOs like this, they can both satisfy the purpose of the builder pattern, more simply, and with broader value/usefulness. Furthermore this approach solves the inheritance complexity the builder pattern results in:

public class SslCert {
   public NetworkAddress Authority;
   public byte[] PrivateKey;
   public byte[] PublicKey;
}

public class FtpsClient extends FtpClient {
   public FtpsClient(NetworkAddress address, SslCert cert) {
      super(address);
      ...
   }
}

You may find the DTO isn't always cohesive, or to make the groupings of properties cohesive they need to be broken across multiple DTOs - this isn't really a problem. If your object requires 18 properties and you can make 3 cohesive DTOs with those properties, you've got a simple construction that meets the builders purposes, and then some. If you can't come up with cohesive groupings, this may be a sign your objects are not cohesive if they have properties that are so completely unrelated - but even then making a single non-cohesive DTO is still preferable due to the simpler implementation plus resolving your inheritance problem.


How to improve the builder pattern

Ok so all of the rimble ramble aside, you have an issue and are looking for a design approach to solve it. My suggestion: inheriting classes can simply have a nested class that inherits from the builder class of the super class, so the inheriting class has basically the same structure as the super class and has a builder pattern that should function precisely the same with the additional functions for the additional properties of the sub-class..


When it is a good idea

Ranting aside, the builder pattern has a niche. We all know it because we all learned this particular builder at one point or another: StringBuilder - here the purpose is not simple construction, because strings couldn't be easier to construct and concatenate etc. This is a great builder because it has a performance benefit.

The performance benefit is thusly: You have a bunch of objects, they're of an immutable type, you need to collapse them down to one object of an immutable type. If you do it incrementally you will here many intermediary objects created, so doing it all at once is far more performant and ideal.

So I think the key of when it is a good idea is in the problem domain of the StringBuilder: Needing to turn multiple instances of immutable types into a single instance of an immutable type.

share|improve this answer
    
I don't think your given example satisfies either rule. There's nothing stopping me creating a Cfg in an invalid state, and while the parameters have been moved out of the ctor they've just been moved to a less idiomatic and more verbose place. fooBuilder.withBar(2).withBang("Hello").withBaz(someComplexObject).build() offers a succinct API for building foos and can offer actual error checking in the builder itself. Without the builder the object itself has to check its inputs, which means we're no better off than we used to be. –  Phoshi Feb 17 at 16:10
    
DTOs can have their properties validated in numerous ways declaratively with annotations, on the setter, however you want to go about it - validation is a separate problem and in his builder approach he shows validation occuring in the constructor, that same logic would fit perfectly well in my approach. However it would generally be better to use the DTO to have it validate because as I show - the DTO can be used to construct multiple types and so having validation on it would lend itself to validating multiple types. The builder only validates for the one particular type it's made for. –  Jimmy Hoffa Feb 17 at 21:15
    
Perhaps the most flexible way would be to have a static validation function in the builder, which accepts a single Fieldable parameter. I would call this validation function from the ToBeBuilt constructor, but it could be called by anything, from anywhere. This eliminates the potential for redundant code, without forcing a specific implementation. (And there's nothing to stop you from passing in individual fields to the validation function, if you don't like the Fieldable concept--but now there would be at least three places in which the field-list would have to be maintained.) –  aliteralmind Feb 17 at 21:29
    
+1 And an a class that has too many dependencies in its constructor is obviously not cohesive enough and should be refactored into smaller classes. –  Basilevs Feb 18 at 3:21
    
@JimmyHoffa: Ah, I see, you just omitted that. I'm not sure I see the difference between this and a builder, then, other than this passes a config instance into the ctor instead of calling .build on some builder, and that a builder has a more obvious path for correctness checking on all the data. Each individual variable could be within its valid ranges, but invalid in that particular permutation. .build can check this, but passing the item into the ctor requires error checking inside the object itself--icky! –  Phoshi Feb 18 at 9:24

protected by World Engineer Feb 14 at 16:31

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.