09 - Iterator & Composite Patterns
09 - Iterator & Composite Patterns
2
public class MenuItem {
String name;
String description;
Check out the Menu Items
boolean vegetarian; At least Lou and Mel agree on the
double price;
implementation of the MenuItems.
public MenuItem(String name, String description,
boolean vegetarian, double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
}
3
Lou’s Menu implementation
public class PancakeHouseMenu implements Menu {
ArrayList menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList();
addItem(“Blueberry Pancakes”,
“Pancakes made with fresh blueberries”, true, 3.49);
addItem(“Waffles”,
“Waffles, with your choice of blueberries or strawberries”, true, 3.59);
}
public void addItem(String name, String description,
boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public ArrayList getMenuItems() {
return menuItems;
}
// other menu methods here
} 4
public class DinerMenu implements Menu {
static final int MAX_ITEMS = 6;
and Mel’s
int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem(“Vegetarian BLT”, “(Fakin’) Bacon with lettuce & tomato on whole wheat”, true, 2.99);
addItem(“BLT”, “Bacon with lettuce & tomato on whole wheat”, false, 2.99);
addItem(“Soup of the day”, “Soup of the day, with a side of potato salad”, false, 3.29);
addItem(“Hotdog”, “A hot dog, with saurkraut, relish, onions, topped with cheese”, false, 3.05);
// a couple of other Diner Menu items added here
}
public void addItem(String name, String description, boolean vegetarian, double price)
{
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.err.println(“Sorry, menu is full! Can’t add item to menu”);
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}
public MenuItem[] getMenuItems() {
return menuItems;
}
// other menu methods here
}
5
What’s the problem with having two different
menu representations?
Imagine the Java-enabled
waitress specifies that The Java-Enabled Waitress Specification
▪ she can print a custom
menu for customers on
demand,
▪ and even tell you if a
menu item is vegetarian
without having to ask
the cook
now that’s an innovation!
6
Let’s start by stepping through how we’d implement the printMenu() method:
1. To print all the items on each menu, you’ll need to call the getMenuItem()
method on the PancakeHouseMenu and the DinerMenu to retrieve their
respective menu items. Note that each returns a different type:
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
ArrayList breakfastItems = pancakeHouseMenu.getMenuItems();
8
W Our situation is:
▪ Mel and Lou don’t want to change their implementations because it would mean
H rewriting a lot of code that is in each respective menu class.
▪ But if one of them doesn’t give in, then we’re going to have the job of implementing a
A Waitress that is going to be hard to maintain and extend.
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
}
11
4. Let’s try it on the Array too:
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
}
12
Meet the Iterator Pattern
The first thing you need to know about the
Iterator Pattern is that it relies on an
interface called Iterator. Here’s one possible
Iterator interface:
14
Reworking the Diner Menu with Iterator
public class DinerMenu implements Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
// constructor here
// addItem here
15
public class Waitress {
PancakeHouseMenu pancakeHouseMenu;
DinerMenu dinerMenu;
public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
System.out.println(“MENU\n----\nBREAKFAST”);
printMenu(pancakeIterator);
System.out.println(“\nLUNCH”);
printMenu(dinerIterator);
}
private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + “, “);
System.out.print(menuItem.getPrice() + “ -- “);
System.out.println(menuItem.getDescription());
} Fixing up the
}
// other methods here Waitress code
}
16
Testing our code
public class MenuTestDrive {
public static void main(String args[]) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();
17
What have we done so far?
Hard to Maintain New, Hip
Waitress Implementation Waitress Powered by Iterator
The Menus are not well encapsulated; we can The Menu implementations are now encapsulated.
see the Diner is using an Array and the The Waitress has no idea how the Menus hold
Pancake House an ArrayList. their collection of menu items.
We need two loops to iterate through the All we need is a loop that polymorphically handles any
MenuItems collection of items as long as it implements Iterator.
The Waitress is bound to concrete classes The Waitress now uses an interface (Iterator).
(MenuItem[] and ArrayList).
The Waitress is bound to two different The Menu interfaces are now exactly the same and, uh oh,
concrete Menu classes, despite their we still don’t have a common interface, which means the
interfaces being almost identical. Waitress is still bound to two concrete Menu classes.
We’d better fix that.
18
Bird’s eye view
Before we clean things up, let’s
get a bird’s eye view of our current
design.
19
Making some improvements...
Okay, we know the interfaces of PancakeHouseMenu
and DinerMenu are exactly the same and yet we haven’t
defined a common interface for them. So, we’re going to
do that and clean up the Waitress a little more.
First, let’s check out the java.util.Iterator
interface:
there are no
Dumb Questions
Q: What if I don’t want to provide the ability to remove Q: How does remove() behave under multiple threads that
something from the underlying collection of objects? may be using different iterators over the same collection of
objects?
A: The remove() method is considered optional. You don’t have
to provide remove functionality. But, obviously you do need to A: The behavior of the remove() is unspecified if the
provide the method because it’s part of the Iterator interface. If collection changes while you are iterating over it. So you
you’re not going to allow remove() in your iterator you’ll should be careful in designing your own multithreaded code
want to throw the runtime exception when accessing a collection concurrently.
java.lang.UnsupportedOperationException. The Iterator API
documentation specifies that this exception may be thrown from
remove() and any client that is a good citizen will check for
this exception when calling the remove() method.
20
Cleaning things up with java.util.Iterator
Let’s start with the PancakeHouseMenu, changing it over to
java.util.Iterator is going to be easy.
We just delete the PancakeHouseMenuIterator class, add an
import java.util.Iterator to the top of PancakeHouseMenu
and change one line of the PancakeHouseMenu:
import java.util.Iterator;
...
21
import java.util.Iterator;
public class DinerMenuIterator implements Iterator {
MenuItem[] list;
int position = 0;
23
import java.util.Iterator;
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;
public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
System.out.println(“MENU\n----\nBREAKFAST”);
printMenu(pancakeIterator);
System.out.println(“\nLUNCH”);
printMenu(dinerIterator);
}
private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + “, “);
System.out.print(menuItem.getPrice() + “ -- “);
System.out.println(menuItem.getDescription());
}
}
// other methods here
}
24
What does this get us?
The PancakeHouseMenu and DinerMenu classes implement an
interface, Menu. Waitress can refer to each menu object using the
interface rather than the concrete class. So, we’re reducing the
dependency between the Waitress and the concrete classes by
“programming to an interface, not an implementation.”
25
Bird’s eye view
26
Iterator Pattern defined
▪ Once you have a uniform way of accessing the elements
of all your aggregate objects, you can write
polymorphic code that works with any of these
aggregates.
▪ The other important impact on your design is that the
Iterator Pattern takes the responsibility of traversing
elements and gives that responsibility to the iterator
object, not the aggregate object.
▪ This not only keeps the aggregate interface and
implementation simpler, it removes the responsibility
for iteration from the aggregate and keeps the
aggregate focused on the things it should be focused on
(managing a collection of objects), not on iteration.
27
Class Diagram
28
there are no
Dumb Questions
Q: Could I implement an Iterator that can go backwards as Q: If I’m using Java, won’t I always want to use the
well as forwards? java.util.Iterator interface so I can use my own
iterator implementations with classes that are already using
A: Definitely. In that case, you’d probably want to add two
the Java iterators?
methods, one to get to the previous element, and one to tell
you when you’re at the beginning of the collection of A: Probably. If you have a common Iterator interface, it will
elements. Java’s Collection Framework provides another certainly make it easier for you to mix and match your own
type of iterator interface called ListIterator. This aggregates with Java aggregates like ArrayList and Vector.
iterator adds previous() and a few other methods to the But remember, if you need to add functionality to your
standard Iterator interface. It is supported by any Iterator interface for your aggregates, you can always extend
Collection that implements the List interface. the Iterator interface.
Q: You said we can write “polymorphic code” using an Q: I’ve seen an Enumeration interface in Java; does that
iterator; can you explain that more? implement the Iterator Pattern?
A: When we write methods that take Iterators as A: We talked about this in the Adapter Pattern. Remember? The
parameters, we are using polymorphic iteration. That means java.util.Enumeration is an older implementation of
we are creating code that can iterate over any collection as Iterator that has since been replaced by
long as it supports Iterator. We don’t care about how the java.util.Iterator. Enumeration has two methods,
collection is implemented, we can still write code to iterate hasMoreElements(), corresponding to hasNext(), and
over it. nextElement(), corresponding to next().
29
Single Responsibility
What if we allowed our aggregates to implement their internal collections and related
operations AND the iteration methods? Well, we already know that would expand the number
of methods in the aggregate, but so what? Why is that so bad?
30
31
public class CafeMenu {
Hashtable menuItems = new Hashtable();
public CafeMenu() {
addItem(“Veggie Burger and Air Fries”,
“Veggie burger on a whole wheat bun, lettuce, tomato, and fries”,
true, 3.99);
addItem(“Soup of the day”,
“A cup of the soup of the day, with a side salad”,
false, 3.69);
addItem(“Burrito”,
“A large burrito, with whole pinto beans, salsa, guacamole”,
true, 4.29);
}
public void addItem(String name, String description,
boolean vegetarian, double price)
{
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.put(menuItem.getName(), menuItem);
}
public Hashtable getItems() {
}
return menuItems;
Taking a look at the
} Café Menu 32
Sharpen your pencil
Before passing this slide, quickly jot down the three things we have to do
to this code to fit it into our framework
33
Integrating the Cafe Menu into our framework is easy.
Reworking the Café Menu code Why? Because Hashtable is one of those Java
collections that supports Iterator.
But it’s not quite the same as ArrayList...
public class CafeMenu implements Menu {
Hashtable menuItems = new Hashtable();
35
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;
Menu cafeMenu;
public Waitress(Menu pancakeHouseMenu, Menu dinerMenu, Menu cafeMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
this.cafeMenu = cafeMenu;
}
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
Iterator cafeIterator = cafeMenu.createIterator();
System.out.println(“MENU\n----\nBREAKFAST”);
printMenu(pancakeIterator);
System.out.println(“\nLUNCH”);
printMenu(dinerIterator);
System.out.println(“\nDINNER”);
printMenu(cafeIterator);
}
private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + “, “);
System.out.print(menuItem.getPrice() + “ -- “);
System.out.println(menuItem.getDescription());
}
}
} Adding the Café Menu to the Waitress
36
public class MenuTestDrive {
public static void main(String args[]) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();
CafeMenu cafeMenu = new CafeMenu();
Waitress waitress = new Waitress(pancakeHouseMenu, dinerMenu, cafeMenu);
waitress.printMenu();
}
Breakfast, lunch
AND dinner 37
What did we do?
38
We decoupled the Waitress....
39
. . . and we made the Waitress more extensible
40
But there’s more!
41
Iterators and Collections
Java Collections Framework
implements the
java.util.Collection interface
42
Code The Chefs have decided that they want to be able to alternate their lunch menu items; in other words,
they will offer some items on Monday, Wednesday, Friday and Sunday, and other items on Tuesday,
Magnets Thursday, and Saturday. Someone already wrote the code for a new “Alternating” DinerMenu Iterator
so that it alternates the menu items, but they scrambled it up and put it on the fridge in the Diner as a
joke. Can you put it back together? Some of the curly braces fell on the fl oor and they were too small
to pick up, so feel free to add as many of those as you need.
43
import java.util.Iterator; Code Magnets
import java.util.Calendar;
public class AlternatingDinerMenuIterator implements Iterator {
Solutions
MenuItem menuItem = items[position];
position = position + 2;
return menuItem;
public AlternatingDinerMenuIterator(MenuItem[] items) {
this.items = items;
Calendar rightNow = Calendar.getInstance();
position = rightNow.get(Calendar.DAY_OF_WEEK) % 2; }
public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
} }
System.out.println(“MENU\n----\nBREAKFAST”);
printMenu(pancakeIterator);
System.out.println(“\nLUNCH”);
printMenu(dinerIterator);
System.out.println(“\nDINNER”);
printMenu(cafeIterator);
}
It’s not the Waitress’ fault. We have done a great job of decoupling the menu implementation
and extracting the iteration into an iterator. But we still are handling the menus with
separate, independent objects – we need a way to manage them together.
45
public class Waitress {
ArrayList menus;
public Waitress(ArrayList menus) {
this.menus = menus;
}
public void printMenu() {
Iterator menuIterator = menus.iterator();
while(menuIterator.hasNext()) {
Menu menu = (Menu) menuIterator.next();
printMenu(menu.createIterator());
}
}
void printMenu(Iterator iterator) {
while (iterator.hasNext()) { Sounds like the chef is on to something.
MenuItem menuItem = (MenuItem) iterator.next();
System.out.print(menuItem.getName() + “, “); Let’s give it a try:
System.out.print(menuItem.getPrice() + “ -- “);
System.out.println(menuItem.getDescription());
}
} This looks pretty good, although we’ve lost the names of the
} menus, but we could add the names to each menu.
46
Just when we thought it was safe . . .
Now they want to add a dessert submenu.
▪ Now we have to support not only multiple menus,
but menus within menus.
▪ It would be nice if we could just make the dessert
menu an element of the DinerMenu collection.
▪ But that won’t work as it is now implemented.
47
What we want
(something like this):
The reality is that we’ve reached a level of complexity such that if we don’t
rework the design now, we’re never going to have a design that can accommodate
further acquisitions or submenus.
51
The Composite Pattern allows us to build
structures of objects in the form of trees that
contain both compositions of objects and
individual objects as nodes.
59
One last thing before we write our test drive.
Let’s get an idea of what the menu composite is going to look like at runtime:
60
Okay, now we just need a test drive. Unlike our previous version, we’re going
Now for the test drive... to handle all the menu creation in the test drive. We could ask each chef to
give us his new menu, but let’s get it all tested first. Here’s the code:
public class MenuTestDrive {
public static void main(String args[]) {
MenuComponent pancakeHouseMenu = new Menu(“PANCAKE HOUSE MENU”, “Breakfast”);
MenuComponent dinerMenu = new Menu(“DINER MENU”, “Lunch”);
MenuComponent cafeMenu = new Menu(“CAFE MENU”, “Dinner”);
MenuComponent dessertMenu = new Menu(“DESSERT MENU”, “Dessert of course!”);
MenuComponent allMenus = new Menu(“ALL MENUS”, “All menus combined”);
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);
// add menu items here
dinerMenu.add(new MenuItem( “Pasta”,
“Spaghetti with Marinara Sauce, and a slice of sourdough bread”, true, 3.89));
dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem( “Apple Pie”,
“Apple pie with a flakey crust, topped with vanilla icecream”, true, 1.59));
// add more menu items here
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
}
61
Test drive
62
Flashback to Iterator
You know that we are already using Iterator in our internal Now we need to implement this method in the Menu and
implementation of the print() method, but we can also MenuItem classes:
allow the Waitress to iterate over an entire composite if
she needs to, for instance, if she wants to go through the
entire menu and pull out vegetarian items. public class Menu extends MenuComponent {
Iterator iterator = null;
// other code here doesn’t change
To implement a Composite iterator, let’s add a
createIterator() method in every component. We’ll public Iterator createIterator() {
start with the abstract MenuComponent class: if (iterator == null) {
iterator = new CompositeIterator(
menuComponents.iterator());
}
return iterator;
}
}
With this code we are implementing an external iterator so there is a lot more to keep
track of. For starters, an external iterator must maintain its position in the iteration
so that an outside client can drive the iteration by calling hasNext() and next().
But in this case, our code also needs to maintain that position over a composite,
recursive structure. That’s why we use stacks to maintain our position as we move
up and down the composite hierarchy.
65
The Null Iterator
Okay, now what is this Null Iterator all about?
Think about it this way: a MenuItem has nothing to iterate over,
right? So how do we handle the implementation of its
The second choice certainly seems better.
createIterator() method?
Let’s call it NullIterator and implement it.
Well, we have two choices:
68
▪ try/catch is meant for error handling,
not program logic.
What are our other options?