What would you do if your client asked you to find all suppliers in a list that are located in Alberta? We could create a new Supplier record book but by now you should be seeing the pattern. Whenever the type differs, think about creating a generic class that can extract the essence of the algorithm without worrying about the type. Lets create a generic RecordBook class that can filter by any specification:
public class RecordBook<T>
{
private IList<T> list;
public RecordBook(IList<T> list)
{
this.list = list;
}
public IEnumerable<T> Filter(ISpecification<T> specification)
{
foreach (T item in list)
{
if (specification.IsSatisfiedBy(item))
yield return item;
}
}
}
Our Filter method is actually a C# implementation of one of the big 3 in functional programming...Filter!
Our client is happy with our work and we as developers are satisfied that we can respond to change quickly and filter by and type and specification our client can dream up. Our client then throws us a curveball, they want to see all the customers in the list formatted with their last name before their first name. Lets drive out what this would look like with a test:
[Test]
public void ShouldFormatCustomerNameWithLastNameBeforeFirstName()
{
IList<Customer> recordBook = new List<Customer>();
recordBook.Add(new Customer(new DateTime(2007, 04, 29), "Steven", "Rockarts"));
RecordBook<Customer> customerRecordBook = new RecordBook<Customer>(recordBook);
IList<string> formattedResult =
new List<string>(customerRecordBook.Map<Customer, string>(recordBook, ReverseFullName));
Assert.AreEqual("Rockarts Steven", formattedResult[0]);
}
private string ReverseFullName(Customer customer)
{
return string.Format("{0} {1}", customer.LastName, customer.FirstName);
}
There is a lot going on in this test so let me try and explain it before showing the Map method. The Map method takes an generic input (in our case a Customer object) and an generic output (a string). It then takes the list that it is going to convert (recordBook) and a method that converts a Customer to a string (ReverseFullName). The result is a list of strings with the customer's name reversed. Here is the implementation:
public IEnumerable<TOutput> Map<TInput, TOutput>(IList<TInput> input, Converter<TInput, TOutput> converter)
{
foreach (TInput item in input)
{
yield return converter(item);
}
}
This is the C# implementation of the Map higher order function from functional programming. I will leave it up to you the reader to implement the last of the big three from functional programming (Reduce or Fold). Also try and think about how you would create a function to reverse all customers names who are active. Maybe once the dust settles after DevTeach I will post the solution.