Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
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
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.
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.