Time for the next part in our series, the Iterator Pattern.

Let's start with the definition: "Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation."

Recently, we have received a request to add the notion of orders to our game. To this end, we created a new class, called TacticalMove which represents an order.

TacticalMove Class

We have asked two of our junior developers to create a class for each faction which contains all orders for a given faction.

After a while, they came up with the following implementations:

EVA and LEGION Classes

As you can see, our developers both took a different approach to the problem.

One developer used a List to store the orders, while the other stored them in a fixed-size array.

If we want to create a WorldOrders class, which is able to display all orders of both factions, it would look a bit like this:

Initial WorldOrders Class

As you notice from the red colors, this initial approach has quite a few flaws.

First of all, we are programming against concrete implementations again, Eva and Legion.

We are also showing the internals of our classes to the outside world, since we have both collection representations showing.

And lastly, because we are pushing our internal storage through, we have two different kind of loops to display them. (Forget the foreach operator exists for a moment)

Let's fix this last part by introducing a new interface, called Iterator which has a HasNext() and Next() method, designed to iterate over a collection.

When we have this interface, we need to create two new iterators for each implementation, one holding a List internally, while the other is storing the array.

Iterator Implementations

The only thing we need to change to our Eva and Legion class now is to remove the GetOrders() method and replace it by the GetIterator() method.

GetIterator Methods

Having done this change, allows us to go back to the WorldOrders and replace the two different loops by one identical loop.

Iterator Usage

We still need to pull out the concrete Eva and Legion classes however. Let's do this by introducing a new interface, called IFactionAI.

IFactionAI

When we implement this new interface to our Eva and Legion classes, we have decoupled the WorldOrders class from them.

Taking a look at the class diagram, with the official names, we can now see WorldOrders is only working with Iterators and interfaces.

Iterator Pattern Class Diagram

And that's the Iterator Pattern in action!

But wait, just like the Observer Pattern, we can create it ourselves, or we can use some of the features from the .NET Framework!

Just like we used events earlier on, we can use IEnumerator and IEnumerable this time.

Refactoring our code to use these, results in the following classes remaining:

IEnumerable and IEnumerator

As you can see, there is no more LegionIterator either, since List provides a GetEnumerator method itself, which we simply call.

Time to test all this code we just wrote and try it out.

Testing IEnumerable and IEnumerator

I've uploaded the solution again. Since this was quite a long article, have a look at the code, I've placed the code for each step in seperate folders so you can compare the changes between steps.

Some additional information on the Iterator Pattern:

 
Reacties: 2
 
  • Matt

    How is this different than the IEnumerable pattern baked into .NET? It doesn't make sense to re-implement it when you can take full advantage of IEnumerable, support for foreach, etc.

     
     
  • Hi Matt,

    This isn't any different. This series is about Design Patterns in general, using C# to illustrate them in practice.

    As such, I have described what an Iterator is, and if you read a bit lower in the article, refactored the code to use the IEnumerable/IEnumerator pattern, which is also the resulting code in the uploaded solution.

     
     
  • Reageer
    Items aangeduid met * zijn verplicht. (Naam, Email, Commentaar)
    Enkele items ontbreken of zijn fout ingevuld.
     
     
     
    Om zeker te zijn dat je geen computer bent, typ de onderstaande tekst over.