Multiple Views (redux)

Written by Brett Veenstra

Jeremy posted an article on retrieving filtered results on collections, using the perennial Animal kingdom example. Great post.

As I was looking at this again, it just started looking “smelly”. First thought, can we do this cleaner with Generics?

public IEnumerable AnimalsList() where AnimalType:class
{
  foreach (IAnimal animal in _animals)
  {
    AnimalType testAnimal = animal as AnimalType;
    if (testAnimal != null)
      yield return testAnimal;
  }
}

Oh yeah… generics are just the coolest thing.

But, this still smelled to me (I didn’t like all the filter code inside the function). Sprinkling some functional programming goodness (thank you Dustin), it turned into this.

public delegate bool FilterAnimalPredicate(System.Type type);

/// snip

public IEnumerable AnimalList(FilterAnimalPredicate filter)
{
  foreach (IAnimal animal in _animals)
  {
    if (filter(animal.GetType()))
    {
      yield return animal;
    }
  }
}

Admittedly, I have cheated … we are no longer using a Property, and hence the unit tests had to change. But I’d take this approach as it feels much cleaner.

Thanks again to Jeremy for sparking this post.

Here’s the final Test Code:

using System;
using System.Collections;
using System.Collections.Generic;
using MbUnit.Framework;

namespace MultipleIterators.Test
{
    [TestFixture]
    public class ZooTest
    {

        private static bool IsElephant(System.Type type)
        {
            return (type is Elephant);
        }

        private static bool IsZebra(System.Type type)
        {
            return (type is Zebra);
        }

        [Test]
        public void Can_add_elephants_to_the_zoo()
        {
            Zoo zoo = new Zoo();
            zoo.AddAnimal(new Elephant());
            int animalCounter = 0;

            foreach (IAnimal animal in zoo.Animals){
                animalCounter++;
            }
            Assert.AreEqual(1, animalCounter);
        }

        [Test]
        public void Can_add_zebras_to_the_zoo()
        {
            Zoo zoo = new Zoo();
            zoo.AddAnimal(new Zebra());
            int animalCounter = 0;

            foreach (IAnimal animal in zoo.Animals){
                animalCounter++;
            }
            Assert.AreEqual(1, animalCounter);
        }

        [Test]
        public void Can_get_just_the_zebras_out_of_the_zoo()
        {
            Zoo zoo = new Zoo();
            zoo.AddAnimal(new Zebra());
            zoo.AddAnimal(new Elephant());

            foreach (Zebra zebra in zoo.AnimalsList<Zebra>()){
                Assert.IsTrue(zebra is Zebra);
            }
        }

        [Test]
        public void Can_get_just_the_elephants_out_of_the_zoo()
        {
            Zoo zoo = new Zoo();
            zoo.AddAnimal(new Zebra());
            zoo.AddAnimal(new Elephant());

            foreach (Elephant elephant in zoo.AnimalsList<Elephant>()){
                Assert.IsTrue(elephant is Elephant);
            }
        }

        [Test]
        public void Can_get_just_the_elephants_out_of_the_zoo_using_predicate()
        {
            Zoo zoo = new Zoo();
            zoo.AddAnimal(new Zebra());
            zoo.AddAnimal(new Elephant());

            foreach (Elephant elephant in zoo.AnimalList(IsElephant)){
                Assert.IsTrue(elephant is Elephant);
            }
        }
    }
}