You can write an iterator that “concatenates” your child’s iterators, even lazily. The next() call on the Song iterator will then expand through the Section and MusicComponent and finally deliver the next MusicTime .
Guava makes this easy. Make MusicComponent a Iterable<MusicTime> and implement iterator() as:
@Override public Iterator<MusicTime> iterator() { return Iterables.concat(getChildren()).iterator(); }
Since all children are MusicComponent elements and therefore themselves implement Iterable<MusicTime> , the Song iterator will be a concatenation of Section iterators, which themselves are concatenations of MusicTime iterators.
This last iterator is a special case. The MusicTime iterator should return only once:
@Override public Iterator<MusicTime> iterator() { return Iterators.singletonIterator(this); }
Alternatively, the Section iterator can be replaced by:
@Override public Iterator<MusicTime> iterator() { return getChildren().iterator(); }
In this case, the iteration becomes as simple as:
for (MusicTime time : song) { player.play(time); }
Now you can perform any operation (playback, counting the total duration, ...) without re-implementing recursion.
There are alternative solutions for your problem, but it all comes down to choosing a design. For example, you can use the play method on MusicComponent , which Song and Section will implement, invoking play for all your children. This is a simple recursive implementation, but you must repeat the recursion for all operations that you intend to add to the MusicComponent (e.g. play , getTotalDuration , ...).
If you need more flexibility, you can use the visitor design template and make your game a game visitor (for example, PlayVisitor ). The advantage of this is that you can control the iteration order within the visitor, but it makes it difficult to add new implementations of MusicComponent .