Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

The Java Enclosing Class as an Implementation of the Builder Pattern

  • Print
  • Feedback

Some language features are just plain ugly. The first time you see them, you say to yourself, "If I ever have to use that, there's something wrong with my design." And most of the time you'd be right. Languages tend to be designed to fit the greatest common factor, which widens their appeal. Only problem is there's a lot of bad design in that GCF.

Enter Java nested classes. The first time I laid eyes on these, I couldn't imagine a single use for them that I wouldn't prefer to implement some other way.

Then I noticed something very unique about the way Java handles nested classes. Unless a nested class is declared as a static member of its enclosing class, an instance of the enclosing class must exist before the nested class can be instantiated. For example: EnclosingClass.NestedClass nestedInstance = enclosingInstance.new NestedClass();

Those of you familiar with the Builder Pattern may already be thinking what I was. That enclosing class looks a lot like a Builder with the nested class as its Product! It turns out that an enclosing class can indeed be used in this way. In some cases, it can even provide stronger encapsulation compared to a traditional Builder implementation. In this article we're going to find out how by exploring three examples of implementing run-time composition for a given Product.

A Self-Composing Product

The simplest approach is to let the Product build itself. You can achieve run-time composition by passing an inventory of parts to the constructor. The Product can then decide what parts to use based on availability and some internal rules. Suppose, for example, you want to build meals. A meal is composed of courses which can vary by type (e.g. steak versus fish) and number (e.g. 7 courses versus 3). Your Product interface might look something like this:

package enclosingclassasbuilder;
                                       public interface Meal {
                                       String nextCourse(); //consume the next course
                                       }
                                     

Next you need an inventory of parts. For brevity's sake let's just whip up something simple and obtuse:

package
                                       enclosingclassasbuilder;
                                       import java.util.ArrayList;
                                       public class Refrigerator {
                                       public ArrayList<String> inventory = new ArrayList<String>();
                                       }
                                     

Now you can implement the Meal interface as a self-composing Product like so:

package enclosingclassasbuilder;
                                       import java.util.ArrayList;
                                       public class SelfComposingMeal implements Meal {
                                       private ArrayList<String> courses = new ArrayList<String>();
                                       public SelfComposingMeal(Refrigerator fridge) {
                                       //rules for selecting entree
                                       if( fridge.inventory.contains("steak") ) {
                                       courses.add("steak");
                                       fridge.inventory.remove("steak");
                                       }
                                       else if( fridge.inventory.contains("fish") ) {
                                       courses.add("fish");
                                       fridge.inventory.remove("fish");
                                       }
                                       //rules for selecting side
                                       if( fridge.inventory.contains("potato") ) {
                                       courses.add("potato");
                                       fridge.inventory.remove("potato");
                                       }
                                       else if( fridge.inventory.contains("asparagus") ) {
                                       courses.add("asparagus");
                                       fridge.inventory.remove("asparagus");
                                       }
                                       //rules for selecting dessert
                                       if( fridge.inventory.contains("cobbler") ) {
                                       courses.add("cobbler");
                                       fridge.inventory.remove("cobbler");
                                       }
                                       else if( fridge.inventory.contains("cake") ) {
                                       courses.add("cake");
                                       fridge.inventory.remove("cake");
                                       }
                                       }
                                       
                                       public String nextCourse() {
                                       if( !courses.isEmpty() ) return courses.remove(0);
                                       return null;
                                       }
                                       }
                                     

While this implementation is very straightforward and easily extensible, it exhibits poor encapsulation. Passing an instance of Refrigerator to the constructor of SelfComposingMeal means that you've got a public or package public inventory floating around out there. Not only are you trusting your users to build their own Meals, you're giving them free access to your Refrigerator! What's to stop them from building a "lobsterfest" Meal that consumes all of your most expensive items in one sitting? Clearly, we need a better solution.

  • Print
  • Feedback