CS 3340 OOP
Exercise 7b: Term Project: Recipes: Composite Design Pattern

Goal: To understand the significance of design patterns, and to apply the Composite Pattern to the recipe application's hierarhical structure.


Tutorial

When working on an application, a particular design issue might arise. How to solve such-and-such problem? You might attempt to solve it yourself, but perhaps others - working in teams - have already faced such a problem and know a state-of-the-art solution. Although your application might be totally new, most applications face certain recurring problems. A Design Pattern is a standard architecture to solve a specific type of problem. In this sense, software becomes re-usable.

The COMPOSITE PATTERN provides a convenient way to structure a hierarchy, and then to iterate throughout the hierarchy and apply the same operation to every element of the hierarchy.

In our recipe application, MenuBook has many FoodMenus, which have many Recipes, which have many Ingredients, which have an Item with a specific number of calories. We want to accumulate all of the getCalories() operation calls in order to determine the entire number of calories in the MenuBook. This problem has a solution: The Composite Design Pattern.

Before applying the pattern to our recipe application, let us examine it in more detail: ex7b.pdf

The UML in the above .pdf file, is implemented in CompositePattern.java, and run with this demo.


Assignment

Now we are ready to implement this pattern for our own recipe application.

In directory planner/gui/domain, code interface FoodElement.java with a long getCalories() method.

Code FoodComposite.java using CompositePattern.java as a reference source.

Recall the UML design for the classes: ex7a.pdf

Note that MenuBook, FoodMenu, Recipe each are parents of children in a 1..* relationship.

That is, each of MenuBook, FoodMenu, Recipe "is a" FoodComposite.

Each of the children, Ingredient, Item, "is a" FoodElement.

The purpose of the Composite Pattern is to implement this hierarchical structure. Make the necessary changes to your classes to satisfy these relationships.

Besides Recipe having many Ingredients, Recipe also has many Steps. You will have to handle this as a special case: in Recipe, add another ArrayList to contain <Step>. Also, code add() and remove() for steps.

More notes on this special case. Recipe has many Ingredients and has many Items. And because Recipe has these two sets, it needs a special case to handle the second set, i.e. Steps.

But it already has the one for Ingredients naturally because Recipe "is a" FoodComposite. And FoodComposite has it's own ArrayList called elements. And that will store the Ingredients for the Recipe, inside the parent class.

So all the FoodComposites inherit the list, and the iterator code. Just Recipe needs an extra one for Steps.

Ex7b-Test1

Compile your code and inside of planner/gui/domain/MenuBook.java uncomment the code for Ex7b-Test1 in the main():

System.out.println("Total Calories: " + mb.getCalories());
and then uncomment the code for Ex7b-Test1 in the constructor:
add(m1);
m1.add(r1);

r1.add(ing1);
r1.add(ing2);
r1.add(ing3);

r1.add(new Step("Grill hamburger meat for 10 minutes"));
r1.add(new Step("After 5 minutes, place on cheese on hamburger"));
r1.add(new Step("After 7 minutes, place top/bottom of bun on grill"));
r1.add(new Step("When done, serve hamburger in bun"));

Note that the above code builds the hierarchy by adding children to parents.

Run your code:

java MenuBook

Ex7b-Test1 Output

Total Calories: 950

Did you get the correct result?

The next stage is to implement toString().

In FoodComposite, code a toString() that accumulates all of the individual Elements. Be sure to add a carriage return ("\n") at that end of each one.

Any child that inherits from FoodComposite, can get a full picture of the list stored inside of FoodComposite by just saying: super.toString().

The Composites MenuBook, Recipe, FoodMenu, each need a toString(). But they have already inherited the toString() that you coded above in FoodComposite.

Recall Ex7a specified the desired output and some comments have been added:

MENU BOOK:                                            // MenuBook's toString() should prepend

Menu: Birthday Dinner (DINNER)                        // Menu's toString() should prepend

Recipe: Hamburgers                                    // Recipe's toString() should prepend
Ingredients:                                          // Recipe should do this
0.25 lb Hamburger Meat                                // this iteration will come out naturally from super.toString()
1.0 regular Hamburger Bun                             // Ingredient's toString() needs to form a particular line here
1.0 slice American Cheese
Steps:                                                // Recipe should do this
1. Grill hamburger meat for 10 minutes                // and iterate thru all Steps
2. After 5 minutes, place on cheese on hamburger      // Step's toString() should just return the text
3. After 7 minutes, place top/bottom of bun on grill
4. When done, serve hamburger in bun


Total Calories: 950                                   // MenuBook's toString() should append

Be careful to use super.toString() in the appropriate places to get FoodComposites accumulating toString().

Ex7b-Test2

In planner/gui/domain/MenuBook, uncomment the code for Ex7b-Test2 in the main():

System.out.println(mb.toString());

Compile and run.

Ex7b-Test2 Output

Does it print the above MENU BOOK?

Now for the last part of the assignment.

FoodMenu, Recipe, Item, Unit should each have a public static HashMap; recall HashMap from Ex6. Be sure the instance variable in all classes is called "hashMap" so Ex7b-Test3 below will compile.

What generic should be stored in the HashMap in FoodMenu? Itself! That is, <String, FoodMenu>, where String will be the menuName. And be sure that this object actually gets stored into the HashMap.

What is the purpose of this? Well, the GUI in Ex7c will need direct access to a list (or table) of FoodMenus. Likewise, for Recipe, Item, Unit. How does menuName get into the HashMap? At construction.

Ex7b-Test3

Uncomment the code for Ex7b-Test3 in the constructor which prints out the keys to the HashMaps. Compile and run again.

Ex7b-Test3 Output

Keys to HashMaps
----------------------
FoodMenu:
  Birthday Dinner
Recipe:
  Hamburgers
Item:
  American Cheese
  Hamburger Meat
  Sugar
  Hamburger Bun
Unit:
  oz
  cup
  regular
  ten
  slice
  lb
----------------------

Did you get the correct results? Note that hash tables are not sorted by keys. So you might get a different ordering, that's OK. Your version of Java is different than the version that produced the above. Your version may have different hash function and/or size.

Turn-In: All 8 Domain Objects (or entire directory: planner), including in MenuBook a commented-out listing of resultant output. Also, submit FoodElement, FoodComposite.


ex7a ex7b ex7c ex7d