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
Page 3 of 3
Now you'd like to restrict access to the courses of a Meal as well as the inventory of parts used to build them. You also want to put the compositional logic back into your Meals to ease the burden on your Builder. You are really high maintenance. Lucky for you, Java's handling of nested classes gives you the power to do both. Here's how:
package enclosingclassasbuilder;
import java.util.ArrayList;
public class EnclosingChef implements Chef
{
private Refrigerator m_ChefsFridge = new Refrigerator();
public EnclosingChef() {
}
public EnclosingChef(Refrigerator fridge) {
while( !fridge.inventory.isEmpty() ) m_ChefsFridge.inventory.add( fridge.inventory.remove(0) );
}
public Meal buildMeal() {
return this.new NestedMeal();
}
public class NestedMeal implements Meal {
private ArrayList<String> courses = new ArrayList<String>();
public NestedMeal() {
//rules for building entree
if( m_ChefsFridge.inventory.contains("steak") ) {
courses.add("steak");
m_ChefsFridge.inventory.remove("steak");
}
else if( m_ChefsFridge.inventory.contains("fish") ) {
courses.add("fish");
m_ChefsFridge.inventory.remove("fish");
}
//rules for building side
if( m_ChefsFridge.inventory.contains("potato") ) {
courses.add("potato");
m_ChefsFridge.inventory.remove("potato");
}
else if( m_ChefsFridge.inventory.contains("asparagus") ) {
courses.add("asparagus");
m_ChefsFridge.inventory.remove("asparagus");
}
//rules for building dessert
if( m_ChefsFridge.inventory.contains("cobbler") ) {
courses.add("cobbler");
m_ChefsFridge.inventory.remove("cobbler");
}
else if( m_ChefsFridge.inventory.contains("cake") ) {
courses.add("cake");
m_ChefsFridge.inventory.remove("cake");
}
}
public String nextCourse() {
if( !courses.isEmpty() ) return courses.remove(0);
return null;
}
}
}
Now we've addressed all of our encapsulation issues. Inventory can only be stocked by a third party, not reduced. Meals can only be consumed by users, not altered.
Notice something else we've accomplished by enclosing Meal within Chef as an instance-bound member. We've introduced the constraint that a Meal cannot exist unless there is a Chef to make it. This is consistent with our requirement of complete Meal control via an intermediary. Life is good.
Is this approach always preferable? I'd be scared if it were. Certainly there are cases where the above implementation would not be appropriate. The most obvious of these being the case where you want to give your users the ability to build their own meals. In this article you've leaned how to implement the Builder Pattern using nested classes in Java. You've seen an example where this provides improved encapsulation compared to a more traditional implementation.
As an exercise, think about how you might add seven-course meals to both EnclosingChef and NonEnclosingChef.
Jason DeMorrow has been using Java since its infancy in the mid 90's. He completed his undergraduate degree in Computer Science at a Big Ten university and has a graduate degree in Information Technology and Operations Management. He currently works as a software architect with a major travel technology company. In his spare time he enjoys contributing to open source projects and going to his friends' board game parties.