Continuing our exploration of design patterns in Ruby and C#, we’re going to dive into the Iterator pattern. Like most design patterns, if you google (or bing) for an example you will run into several different implementations because there are many ways to iterate a collection of objects. In it’s classic (well, classing in the .Net\Java sense) form, the pattern is made of:
- An interface defining the iterator. This typically involves two methods: HasNext() and MoveNext().
- An concrete iterator implementation
- An interface defining an enumerable collection. The only method really needed here would be GetIterator() which will return the iterator object.
- A concrete enumerable implementation.
The book Design Patterns in Ruby actually defines that type of implementation as an external iterator. This form gives you a type of hook into the collection that allows you to pull out an object one at a time and work with it externally, away from the insides of the collection. The other type of implementation that it describes is an internal iterator. This form involves a method that passes logic (think anonymous method in C# or code block in Ruby) into a method on the collection. The collection then internally iterates through it’s items and applies the logic on each item. This is a very common way of working with collections in Ruby and is slowly building adoption in C#. As we work through the examples below, we’ll take a look at both external and internal implementations.
The following examples will consist of a used car sales lot which will consist of a collection of vehicles.
C# Example – External Iterator From Scratch
First off, let’s create a basic Vehicle object.
public class Vehicle { public string Name { get; set; } public Vehicle(string name) { Name = name; } }
Our next step is to create an ISimpleIterator interface which we’re going to make generic so we aren’t tied to a specific type of object. It really only needs two methods, HasNext() which will let us know if we’ve reached the end of the collection, and MoveNext() which will return the item in the next position.
public interface ISimpleIterator<T> { bool HasNext(); T MoveNext(); }
And now we’ll create a concrete implementation of the iterator.
public class SimpleIterator<T> : ISimpleIterator<T> { private int _position = 0; private T[] _items; public SimpleIterator(T[] items) { _items = items; } public bool HasNext() { return _position < _items.Length; } public T MoveNext() { var item = _items[_position]; _position++; return item; } } [/source] <p>Next, let's create our ISimpleEnumerable interface. We'll keep this one generic as well since there's no reason to lock it into only Vehicle objects. Note that it only needs one method that will return a generic ISimpleIterator.</p> public interface ISimpleEnumerable<T> { ISimpleIterator<T> GetIterator(); }
And our enumerable object will be a SalesLot entity. The GetIterator() method will return a strongly typed vehicle iterator.
public class SalesLot : ISimpleEnumerable<Vehicle> { private Vehicle[] _vehicles = { new Vehicle("Toyota Camry"), new Vehicle("Jeep Grand Cherokee"), new Vehicle("Honda CRV") }; public ISimpleIterator<Vehicle> GetIterator() { return new SimpleIterator<Vehicle>(_vehicles); } }
Let’s see what this code would actually look like if we tried to run it. All we have to do is grab the iterator and wrap it in a while loop.
var salesLot = new SalesLot(); var vehiclesIterator = salesLot.GetIterator(); while (vehiclesIterator.HasNext()) Console.WriteLine(vehiclesIterator.MoveNext().Name);
Output:
Toyota Camry
Jeep Grand Cherokee
Honda CRV
C# Example – External Iterator Using Built-In IEnumerable Interface
Our design can be hugely simplified by using the IEnumerable
public class SalesLot : IEnumerable<Vehicle> { private IEnumerable<Vehicle> _vehicles = new List<Vehicle> { new Vehicle("Toyota Camry"), new Vehicle("Jeep Grand Cherokee"), new Vehicle("Honda CRV") }; public IEnumerator<Vehicle> GetEnumerator() { return _vehicles.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
And using it is just as easy as our earlier example.
var salesLot = new SalesLot(); var enumerator = salesLot.GetEnumerator(); while (enumerator.MoveNext()) Console.WriteLine(enumerator.Current.Name);
And any class that implements the IEnumerable interface is also able to be iterated over using the foreach construct.
var salesLot = new SalesLot(); foreach (var vehicle in salesLot) Console.WriteLine(vehicle.Name);
C# Example – Adding an Internal Iterator to the SalesLot Class
The previous examples all used external iterators to navigate the collection of cars. But we could have easily used an internal iterator as well. To do this, we need to add a method to the SalesLot object that will allow us to pass in an anonymous method or lambda expression to apply to each vehicle in the collection.
public class SalesLot { private IEnumerable<Vehicle> _vehicles = new List<Vehicle> { new Vehicle("Toyota Camry"), new Vehicle("Jeep Grand Cherokee"), new Vehicle("Honda CRV") }; public void ForEach(Action<Vehicle> action) { foreach (var vehicle in _vehicles) action(vehicle); } }
And using it becomes just a couple lines of code.
var salesLot = new SalesLot(); salesLot.ForEach(vehicle => Console.WriteLine(vehicle.Name));
C# Example – Internal Iterator Using the Built-In ForEach List Extension Method
With the addition of Linq, we were given a ForEach extension method on generic lists. Using it is exactly the same as our hand-rolled example.
var salesLot = new List<Vehicle> { new Vehicle("Toyota Camry"), new Vehicle("Jeep Grand Cherokee"), new Vehicle("Honda CRV") }; salesLot.ForEach(vehicle => Console.WriteLine(vehicle.Name));
Ruby Example – External Iterator From Scratch
To get started with our Ruby examples, we’ll create our Vehicle object.
class Vehicle attr_accessor :name def initialize(name) @name = name end end
And since Ruby suports duck-typing, we don’t need to create an abstract class or interface to define our iterator. We just need to make sure it has the appropriate methods. Notice that our functions that return a value (such as has_next or move_next) do not need to include the “return” keyword. In Ruby the last line of a mehod is the return value.
class SimpleIterator def initialize(items) @items = items @position = 0 end def has_next @position < @items.length end def move_next item = @items[@position] @position += 1 item end end [/source] <p>We are also free to go ahead with creating the SalesLot class without defining an Enumerable abstraction as long as we include a get_iterator method that returns a SimpleIterator.</p> class SalesLot def initialize @vehicles = [Vehicle.new("Toyota Camry"), Vehicle.new("Jeep Grand Cherokee"), Vehicle.new("Honda CRV")] end def get_iterator SimpleIterator.new(@vehicles) end end
Actually using the class is very similiar to the C# example.
sales_lot = SalesLot.new vehicle_iterator = sales_lot.get_iterator while vehicle_iterator.has_next puts vehicle_iterator.move_next.name end
Output:
Toyota Camry
Jeep Grand Cherokee
Honda CRV
Ruby Example – Creating an Internal Iterator Using the Enumerable Module
While external iterators have their place, Rubyists tend to favor using internal iterators when working with collections. We could easily tack on our own for_each method to the SalesLot object (as we did in the C# example) but instead let’s jump straight into some of the cool things we can leverage in the library. Ruby ships with a handy module called Enumerable that we can include on our SalesLot object which will give us a lot of functionality for free. To use it, we just need to include the Enumerator module and add an “each” method that accepts a code block.
class SalesLot include Enumerable def initialize @vehicles = [Vehicle.new("Toyota Camry"), Vehicle.new("Jeep Grand Cherokee"), Vehicle.new("Honda CRV")] end def each(&amp;amp;amp;amp;block) @vehicles.each(&amp;amp;amp;amp;block) end end
To be able to take advantage of the features given to us from the Enumerable module, our Vehicle needs to add a method defining the “<=>” comparison operator. This method accepts another Vehicle record to compare to and will return -1, 0, or 1 depending on whether the receiver is less than, equal to, or greater than the vehicle being passed in as an argument. For those of you familiar with the IComparable
class Vehicle attr_accessor :name def initialize(name) @name = name end def <=>(other) @name <=> other.name end end
And now we can iterate through our collection in just a couple of lines.
sales_lot = SalesLot.new sales_lot.each {|vehicle| puts vehicle.name}
But that’s just the tip of the iceberg. We are given a lot of added functionality that allows us to search and sort vehicles in the sales lot.
sales_lot.include? "Ford Escape" sales_lot.find_all {|vehicle| vehicle.name.length > 12} sales_lot.sort
And this functionality is available when working with arrays which allows us to do a lot of work with our collections without having to write much code.
vehicles = [Vehicle.new("Toyota Camry"), Vehicle.new("Jeep Grand Cherokee"), Vehicle.new("Honda CRV")] vehicles.each {|vehicle| puts vehicle.name}
While the iterator pattern may look complex in it’s natural form, both Ruby and C# give us a lot of capabilities out of the box. Neither need much (if any) customization to do advanced work with collections.
Next time, we’ll check out the Command pattern!
Leave A Comment