Sei sulla pagina 1di 37

Well-managed Collections: The Iterator Pattern

Luca Vigan
Dipartimento di Informatica Universit di Verona

Laboratorio di Ingegneria del Software 27.05.2011

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

1 / 37

A Problem Description: Array and ArrayList

A Diner and a Pancake House merge, but there is a slight problem... They want to use the Pancake Houses menu as the breakfast menu and Diners menu as the lunch menu, but...
Pancake House uses an ArrayList to hold its menu, Diner uses an Array to hold its menu, and neither one of them is willing to change its implementation... they just have too much code written that depends on them.

Have a look at the code...

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

2 / 37

Pancake House and Diner: Menu implementations

Now lets take a look at what Pancake House and Diner are arguing about. They both have lots of time and code invested in the way they store their menu items in a menu, and lots of other code that depends on it. Implementation of the Pancake House Menu...

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

3 / 37

Implementation of the Pancake House Menu


import j a v a . u t i l . A r r a y L i s t ; public class PancakeHouseMenu { / / Pancake House i s u s i n g an A r r a y L i s t t o s t o r e i t s menu i t e m s . A r r a y L i s t menuItems ; public PancakeHouseMenu ( ) { menuItems = new A r r a y L i s t ( ) ; / Each menu i t e m i s added t o t h e A r r a y L i s t here , i n t h e c o n s t r u c t o r . Each menu i t e m has a name , a d e s c r i p t i o n , whether o r n o t i t s a v e g e t a r i a n item , and t h e p r i c e . / addItem ( "K&B s Pancake B r e a k f a s t " , " Pancakes w i t h scrambled eggs , and t o a s t " , true , 2.99); addItem ( " Regular Pancake B r e a k f a s t " , " Pancakes w i t h f r i e d eggs , sausage " , false , 2.99); addItem ( " B l u e b e r r y Pancakes " , " Pancakes made w i t h f r e s h b l u e b e r r i e s , and b l u e b e r r y syrup " , true , 3.49);

continue ...
Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 4 / 37

Implementation of the Pancake House Menu (2)


addItem ( " W a f f l e s " , " Waffles , w i t h your c h o i c e o f b l u e b e r r i e s o r s t r a w b e r r i e s " , true , 3.59); } public void addItem ( S t r i n g name , S t r i n g d e s c r i p t i o n , boolean v e g e t a r i a n , double p r i c e ) { / To add a menuitem , Pancake House c r e a t e s a new MenuItem o b j e c t , passing i n each argument and then adds i t t o t h e A r r a y L i s t / MenuItem menuItem = new MenuItem ( name , d e s c r i p t i o n , v e g e t a r i a n , p r i c e ) ; menuItems . add ( menuItem ) ; } / Returns t h e l i s t o f menu i t e m s . / public A r r a y L i s t getMenuItems ( ) { r e t u r n menuItems ; } / / o t h e r menu methods here

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

5 / 37

Implementation of the Diner Menu


/ I m p l e m e n t a t i o n o f t h e D i n e r Menu / import j a v a . u t i l . I t e r a t o r ; public class DinerMenu { s t a t i c f i n a l i n t MAX_ITEMS = 6 ; i n t numberOfItems = 0 ; / D i n e r i s u s i n g an A r r a y so he can c o n t r o l t h e max s i z e o f t h e menu and r e t r i e v e menu i t e m s o u t w i t h o u t having t o c a s t h i s o b j e c t s . / MenuItem [ ] menuItems ; public DinerMenu ( ) { menuItems = new MenuItem [ MAX_ITEMS ] ; / L i k e Pancake House , D i n e r c r e a t e s i t s menu i t e m s i n t h e c o n s t r u c t o r , u s i n g t h e addItem ( ) h e l p e r method . / addItem ( " V e g e t a r i a n BLT " , " ( Fakin ) Bacon w i t h l e t t u c e & tomato on whole wheat " , true , 2 . 9 9 ) ; addItem ( " BLT " , " Bacon w i t h l e t t u c e & tomato on whole wheat " , f a l s e , 2 . 9 9 ) ; addItem ( " Soup o f t h e day " , " Soup o f t h e day , w i t h a s i d e o f p o t a t o s a l a d " , f a l s e , 3 . 2 9 ) ;

continue ...
Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 6 / 37

Implementation of the Diner Menu (2)


addItem ( " Hotdog " , "A h o t dog , w i t h s a u r k r a u t , r e l i s h , onions , topped w i t h cheese " , false , 3 . 0 5 ) ; addItem ( " Steamed Veggies and Brown Rice " , " Steamed v e g e t a b l e s over brown r i c e " , true , 3 . 9 9 ) ; addItem ( " Pasta " , " S p a g h e t t i w i t h Marinara Sauce , and a s l i c e o f sourdough bread " , true , 3 . 8 9 ) ; } / addItem ( ) t a k e s a l l t h e parameters necessary t o c r e a t e a MenuItem and i n s t a n t i a t e s one . I t a l s o checks t o make sure we haven t h i t t h e menu s i z e l i m i t . / public void addItem ( S t r i n g name , S t r i n g d e s c r i p t i o n , boolean v e g e t a r i a n , double p r i c e ) { MenuItem menuItem = new MenuItem ( name , d e s c r i p t i o n , v e g e t a r i a n , p r i c e ) ; i f ( numberOfItems >= MAX_ITEMS) System . e r r . p r i n t l n ( " Sorry , menu i s f u l l ! Can t add i t e m t o menu " ) ; else { menuItems [ numberOfItems ] = menuItem ; numberOfItems ++; } } public MenuItem [ ] getMenuItems ( ) { r e t u r n menuItems ; } / / o t h e r menu methods here } Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 7 / 37

Problem of two different menu representations

To see why having two different menu representations complicates things, lets try implementing a client that uses the two menus. Imagine you have been hired by the new company formed by the merger of the Diner and the Pancake House to create a Java-enable waitress. The specication of waitress species that 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. Lets check out the specication, and then step through what it might take to implement the waitress.

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

8 / 37

The Java-Enable Waitress Specication

Java-Enable Waitress Alice


/ / p r i n t s every i t e m on t h e menu printMenu ( ) / / p r i n t s j u s t breakfast items printBreakfastMenu ( ) / / p r i n t s j u s t lunch items printLunchMenu ( ) / / p r i n t s a l l v e g e t a r i a n menu i t e m s printVegetarianMenu ( ) / g i v e n t h e name o f an item , r e t u r n s t r u e i f t h e i t e m s i s v e g e t a r i a n , otherwise , r e t u r n f a l s e / i s I t e m V e g e t a r i a n ( name )

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

9 / 37

The Java-Enable Waitress Specication (2)

Lets start by stepping through how wed implement the printMenu() method: To print all the items on each menu, youll 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 ( ) ; A r r a y L i s t b r e a k f a s t I t e m s =pancakeHouseMenu . getMenuItems ( ) ; DinerMenu DinerMenu = new DinerMenu ( ) ; Menultem [ ] l u n c h l t e m s = DinerMenu . getMenuItems ( ) ;

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

10 / 37

The Java-Enable Waitress Specication (3)


Now, to print out the items from the PancakeHouseMenu, well loop through the items on the breakfastltems Array List. And to print out the Diner items well loop through the Array.
for ( int i = 0; i < breakfastltems . size ( ) ; i ++) {

Menultem menultem = ( Menultem ) b r e a k f a s t l t e m s . g e t ( i ) ; System . o u t . p r i n t ( menuItem . getName { ) + " " ); System . o u t . p r i n t I n ( menultem . g e t P r i c e ( ) + " " ); System . o u t . p r i n t I n ( menultem . g e t D e s c r i p t i o n ( ) ) ; } for ( int i = 0; i < l u n c h l t e m s . l e n g t h ; i ++) {

Menultem menultem = l u n c h l t e m s [ i ] ; System . o u t . p r i n t ( menultem . getName ( ) + " " ); System . o u t . p r i n t I n ( menultem . g e t P r i c e ( ) + " " ); System . o u t . p r i n t I n ( menultem . g e t D e s c r i p t i o n ( ) ) ; }

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

11 / 37

The Java-Enable Waitress Specication (4)

Implementing every other method in the Waitress is going to be a variations of this theme. Were always going to need to get both menus and use two loops to iterate through their items. If another restaurant with a different implementation is acquired then well have three loops.

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

12 / 37

Can we encapsulate the iteration?

Encapsulate what varies!


Its obvious what is changing here: the iteration caused by different collections of objects being returned from the menus. But can we encapsulate this?

Lets work through the idea...

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

13 / 37

Can we encapsulate the Iteration? (2)

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

14 / 37

Can we encapsulate the Iteration? (3)

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

15 / 37

Can we encapsulate the Iteration? (4)

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

16 / 37

Can we encapsulate the Iteration? (5)

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

17 / 37

Meet the Iterator Pattern


Well, it looks like our plan of encapsulating iteration just might actually work; and as youve probably already guessed, it is a Design Pattern called the Iterator Pattern. The rst thing you need to know about the Iterator Pattern is that it relies on an interface called Iterator. Heres one possible Iterator interface...

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

18 / 37

Meet the Iterator Pattern


Now once we have this interface, we can implement Iterators for any kind of collection of objects: arrays, lists, hashtables, ...pick your favorite collection of objects. Lets say we wanted to implement the Iterator for the Array used in the DinerMenu. It would look like this...

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

19 / 37

Adding an Iterator to DinerMenu

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

20 / 37

Adding an Iterator to DinerMenu (2)

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

21 / 37

Reworking the Diner Menu with Iterator


To work the iterator into the DinerMenu, all we need to do is add one method to create a DinerMenulterator and return it to the client:

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

22 / 37

Fixing up the Waitress code

Now we need to integrate the iterator code into the Waitress. We should be able to get rid of some of the redundancy in the process. Integration is pretty straightforward:
1 2

rst we create a printMenu() method that takes an Iterator; then we use the createlterator() method on each menu to retrieve the Iterator and pass it to the new method.

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

23 / 37

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

24 / 37

Testing our code

Its time to put everything to a test. Lets write some test drive code and see how the Waitress works...
public class MenuTestDrive { public s t a t i c void main ( S t r i n g [ ] args ) { Menu pm = new PancakeHouseMenu ( ) ; Menu dm = new DinerMenu ( ) ; W a i t r e s s w = new W a i t r e s s (pm,dm ) ; w . printMenu ( ) ; } }

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

25 / 37

What we have so far

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

26 / 37

Making some improvements...


Okay, we know the interfaces of PancakeHouseMenu and DinerMenu are exactly the same and yet we havent dened a common interface for them. So, were going to do that and clean up the Waitress a little more...
You may be wondering why were not using the Java Iterator interface; we did that so you could see how to build an iterator from scratch. Now that weve done that, were going to switch to using the Java Iterator interface, because well get a tot of leverage by implementing that instead of our home grown Iterator interface.

What kind of leverage? Youll soon see...


Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 27 / 37

Making some improvements...


First, lets check out the java.util.Iterator interface:

Not only does java.util have its own Iterator interface, but ArrayList has an iterator() method that returns an iterator.
In other words, we never needed to implement our own iterator for ArrayList.

However, well still need our implementation for the DinerMenu because it relies on an Array, which doesnt support the iterator() method (or any other way to create an array iterator).
Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 28 / 37

Cleaning things up with java.util.Iterator


Lets start with the PancakeHouseMenu, changing it over to java.util.Iterator is going to be easy:
1 2

We just delete the PancakeHouseMenuIterator class add an import java.util.Iterator to the top of PancakeHouseMenu and change one line of the PancakeHouseMenu.

And thats it, PancakeHouseMenu is done:

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

29 / 37

Now we need to make the changes to allow the DinerMenu to work with java.util.Iterator:

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

30 / 37

We are almost there..


We just need to give the Menus a common interface and rework the Waitress a little. The Menu interface is quite simple:
we might want to add a few more methods to it eventually, like addltem(), but for now we will let the chefs control their menus by keeping that method out of the public interface:

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

31 / 37

Now we need to add an implements Menu to both the PancakeHouseMenu and the DinerMenu class denitions and update the Waitress:

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

32 / 37

What does this get us?


To solve the problem of the Waitress depending on the concrete Menus:
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, were reducing the dependency between the Waitress and the concrete classes by programming to an interface, not an implementation.

To solve the problem of the Waitress depending on the implementation of the MenuItems:
The new Menu interface has one method, createIterator(), that is implemented by PancakeHouseMenu and DinerMenu. Each menu class assumes the responsibility of creating a concrete Iterator that is appropriate for its internal implementation of the menu items.
Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 33 / 37

The Iterator Pattern for the menus

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

34 / 37

The Iterator Pattern dened


Youve already seen how to implement the Iterator Pattern with your very own iterator. Youve also seen how Java supports iterators in some of its collection oriented classes (the ArrayList). Now its time to check out the ofcial denition of the pattern: The Iterator Pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. The pattern gives you a way to step through the elements of an aggregate without having to know how things are represented under the covers. Youve seen that with the two implementations of menus. But the effect of using iterators in your design is just as important.
Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 35 / 37

The Iterator Pattern dened (2)


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, just like the printMenu() method, which doesnt care if the menu items are held in an Array or ArrayList (or anything else that can create an Iterator), as long as it can get hold of an Iterator. 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. Lets check out the class diagram to put all the pieces in context...
Luca Vigan (Universit di Verona) The Iterator Pattern Lab. Ing. del SW, 27.05.2011 36 / 37

The Iterator Pattern: class diagram

Luca Vigan (Universit di Verona)

The Iterator Pattern

Lab. Ing. del SW, 27.05.2011

37 / 37

Potrebbero piacerti anche