You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: modules/Module-02.md
+71Lines changed: 71 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -279,6 +279,77 @@ A final question to consider for comparators is whether a comparator should have
279
279
we could define a `UniversalComparator` that has an enum type field capturing the desired type of comparison. Although this solution is workable, it can lead to code that
280
280
is harder to understand, for reasons explained in Module 3.
281
281
282
+
### Iterating Over An Aggregation
283
+
284
+
In Module 1 I introduced the problem of how to gain access to a collection of objects encapsulated by another object without violating encapsulation and information hiding. One solution proposed was to return copies of the internal state, for example, returning a copy of the `Stack` of cards encapsulated within a `Deck` instance. One issue with this is that it can lead to *coupling* between the precise data structure returned and the clients. For instance, if we choose to return a deck's cards as a list:
285
+
286
+
```
287
+
public Stack<Card> getCards() {...}
288
+
```
289
+
290
+
The clients of the `Deck` will may start relying on the operations defined on a list, or make the assumption that cards are internally stored in a list within a `Deck`. For a cleaner design, it would be best to allow clients access to the internal objects of another objects, without exposing anything about the internal structure of the encapsulating object. This design feature is supported by the concept of an *Iterator*. An iterator is relatively easy to use, but implementing this idea required careful coordination between at least three types of objects, so it's a another great illustration of the effective use of interfaces and polymorphism.
291
+
292
+
To support iteration we must first have a specification of what it means to iterate. As usual, this specification is captured in an interface, in this case the [Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) interface. This interface defines two non-default methods: `hasNext()` and `next()`. So, according to the rules of polymorphism, one a piece of code gains access to a reference to an object of any subtype of `Iterator`, the client code can iterate over it, independently of what the actual class of the object is.
293
+
294
+
To enable iteration over the cards of a `Deck`, let's simply redefine the `getCards` method to return an iterator instead of a list:
295
+
296
+
```
297
+
public Iterator<Card> getCards() {...}
298
+
```
299
+
300
+
This way to print all the cards of a deck, we can do:
301
+
302
+
```
303
+
Iterator<Card> iterator = deck.getCards();
304
+
while( iterator.hasNext() )
305
+
{
306
+
System.out.println(iterator.next());
307
+
}
308
+
```
309
+
310
+
Although this design achieves our decoupling goal and is already pretty good, we can generalize it a bit, to great effect. A first important insight is that in most large programs there will typically be many different types of object that it would be useful to iterate over. Lists are an obvious example. In our case we have a deck. But in practice the list is infinite. In a university management system, there may be a class `CourseSection` that contains `Student` objects, and we would want to iterate over the students in the course, etc. The issue with the iterator system as we have it now, is that every class defines a different way to obtain an iterator. For class `List`, it's through the method `iterator()`. For our `Deck` class, it's through method `getCards()`. Although the behavior in both cases is identical (return an iterator), the *name* of the service is different. We can solve this issue with... an interface, naturally. The [https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html](Iterable) interface specifies the smallest "slice" of behavior necessary to make it possible to iterate over an object. Not surprisingly, to be able to iterate over an object, the only thing we need from this object it that is supplies us with an iterator. So the only non-default method of the `Iterable` interface is `Iterator<T> iterator()`.
311
+
312
+
We can make our `Deck` class iterable by extending the `Iterable` interface and renaming the `getCards()` method to `iterator()`:
313
+
314
+
```
315
+
public class Deck implements Iterable<Card?
316
+
{
317
+
...
318
+
public Iterator<Card> iterator() {...}
319
+
}
320
+
321
+
This way an instance of `Deck` can be supplied anywhere an `Iterable` interface is expected. As it turns out, one of the main ways to use `Iterable` objects is with the Java `forall` loop. In Java the `forall` loop:
322
+
323
+
```
324
+
List<String> theList = ...;
325
+
for( String string : theList )
326
+
{
327
+
System.out.println(string);
328
+
}
329
+
```
330
+
331
+
is just [syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) for
The way the `forall` loop can work, is that under the cover it expects the rightmost part of the loop head to be an instance of a class that is a subtype of `Iterable`.
352
+
282
353
## Reading
283
354
* Textbook 4.1-4.5, 5.1, 5.2, 5.4.3
284
355
* Solitaire v0.3 [PlayingStrategy.java](https://github.com/prmr/Solitaire/blob/v0.3/src/ca/mcgill/cs/stg/solitaire/ai/PlayingStrategy.java) as a simple example of a Strategy interface;
0 commit comments