Thursday, November 22, 2007

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.

Thursday, November 22, 2007 10:33:22 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 

Our Customer comes to us and likes what we have done setting up a way for them to find if a customer is active, but now they want to keep track of all the customers. We come up with the concept of a Customer Record Book and our client seems happy with that:

[Test]
public void ShouldBeAbleToCreateACustomerRecordBook()
{
    IList<Customer> recordBook = new List<Customer>();
    recordBook.Add(new Customer(new DateTime(2007, 04, 29), "Steven", "Rockarts"));
    recordBook.Add(new Customer(new DateTime(1965, 04, 29), "John", "McCarthy"));
    recordBook.Add(new Customer(new DateTime(2006, 04, 29), "Doug", "Armstrong"));
 
    CustomerRecordBook customerRecordBook = new CustomerRecordBook(recordBook);
 
    Assert.IsNotNull(customerRecordBook);
}

public class CustomerRecordBook
{
    private IList<Customer> customerList;
 
    public CustomerRecordBook(IList<Customer> customerList)
    {
        this.customerList = customerList;
    }
}

Our record book does its job of storing customers well but our client wants us to add a way to find all the active customers in it. Our first attempt at creating a way to find all the active customers looks like this:

[Test]
public void ShouldFindAllActiveCustomers()
{
    IList<Customer> recordBook = new List<Customer>();
    recordBook.Add(new Customer(new DateTime(2007, 04, 29), "Steven", "Rockarts"));
    recordBook.Add(new Customer(new DateTime(1965, 04, 29), "John", "McCarthy"));
    recordBook.Add(new Customer(new DateTime(2006, 04, 29), "Doug", "Armstrong"));
 
    CustomerRecordBook customerRecordBook = new CustomerRecordBook(recordBook);
 
    Assert.AreEqual(1, customerRecordBook.Filter(new ActiveCustomerSpecification()).Count);
}

public class CustomerRecordBook
{
    private IList<Customer> customerList;
 
    public CustomerRecordBook(IList<Customer> customerList)
    {
        this.customerList = customerList;
    }
 
    public IList<Customer> Filter(ActiveCustomerSpecification specification)
    {
         IList<Customer> list = new List<Customer>();
 
         foreach (Customer customer in customerList)
         {
             if(customer.IsAnActiveCustomer(specification))
                list.Add(customer);
         }
 
         return list;
     }       
}

The Filter method that we just created takes in the ActiveCustomerSpecification, creates a temporary list and then iterates through the customer list adding the active customers to the temporary list, once it is done it returns the temporary list.  Our test passes but there are a couple of improvements we can make to this code. First of all, we don't need to create a temporary list or use an IList<Customer> we can leverage the yield keyword and IEnumerable<Customer> to make our code tighter:

public IEnumerable<Customer> Filter(ActiveCustomerSpecification specification)
{
    foreach (Customer customer in customerList)
    {
        if (customer.IsAnActiveCustomer(specification))
            yield return customer;
    }
}

This change will break our test since IEnumerable does not contain a definition for count. If we look at the available constructors for the List class we can see that List has a contructor for creating a list from IEnumerable. So we can easily fix our test:

[Test]
public void ShouldFindAllActiveCustomers()
{
    IList<Customer> recordBook = new List<Customer>();
    recordBook.Add(new Customer(new DateTime(2007, 04, 29), "Steven", "Rockarts"));
    recordBook.Add(new Customer(new DateTime(1965, 04, 29), "John", "McCarthy"));
    recordBook.Add(new Customer(new DateTime(2006, 04, 29), "Doug", "Armstrong"));
 
    CustomerRecordBook customerRecordBook = new CustomerRecordBook(recordBook);
 
    Assert.AreEqual(1, new List<Customer>(customerRecordBook.Filter(new ActiveCustomerSpecification())).Count);
}

Now we are able to find all active customers in a list.

Thursday, November 22, 2007 9:46:45 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 

Encapsulating Criteria Using Specifications

"Specification provides a concise way of expressing certain kinds of rules, extricating them from conditional logic and making them explicit in the model."

Eric Evans : Domain Driven Design

In our domain model we have a normal Customer object that looks like this:

public class Customer
{
    private DateTime signUpDate;
    private string firstName;
    private string lastName;
 
    public Customer(DateTime signUpDate, string firstName, string lastName)
    {
        this.signUpDate = signUpDate;
        this.firstName = firstName;
        this.lastName = lastName;
    }
 
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }
 
    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
 
    public DateTime SignUpDate
    {
        get { return signUpDate; }
    }
 
    public bool IsAnActiveCustomer()
    {
        IRange<DateTime> activeRange = new Range<DateTime>(this.signUpDate, this.signUpDate.AddYears(1));
 
        return activeRange.Contains(DateTime.Now);
    }
}

With this code, we have defined a customer as something with a first name, last name, and a signup date. We have also specified (using the IsAnActiveCustomer method) that an active customer is a customer who has signed up with us within the current year.

This method that determines whether or not a customer is active as it stands isn't very flexible. To make the method more flexible we can extract the criteria that determines whether or not a customer is active or not and add it as a parameter to the IsAnActiveCustomer method so that the criteria can be passed in.

In order to encapsulate the active customer we can leverage the specification pattern. Our first attempt at making an active customer specification might look something like this:

public interface ISpecification
{
    bool IsSatisfiedBy(Customer customer);
}
 
public class ActiveCustomerSpecification : ISpecification
{
    public bool IsSatisfiedBy(Customer customer)
    {
        IRange<DateTime> activeRange = new Range<DateTime>(customer.SignUpDate, customer.SignUpDate.AddYears(1));
 
        return activeRange.Contains(DateTime.Now);
    }
}

and in the customer class:

public bool IsAnActiveCustomer(ISpecification activeCustomerSpecification)
{
    return activeCustomerSpecification.IsSatisfiedBy(this);
}

By adding the specification as a parameter to the IsAnActiveCustomer method we have inverted the control of the definition of an active customer to consumers of the customer class. There may be different definitions of what an active customer is in different situations.

We can make one more improvement to the specification class before moving on. What happens if another class wants to leverage the specification interface? They have to create a new specification for their new type...or they can use generics to save themselves the time of writing a new specification interface for every new type. Here is the result of our change:

public interface ISpecification<T>
{
    bool IsSatisfiedBy(T item);
}
 
public class ActiveCustomerSpecification : ISpecification<Customer>
{
    public bool IsSatisfiedBy(Customer customer)
    {
        IRange<DateTime> activeRange = new Range<DateTime>(customer.SignUpDate, customer.SignUpDate.AddYears(1));
 
        return activeRange.Contains(DateTime.Now);
    }
}

public bool IsAnActiveCustomer(ISpecification<Customer> activeCustomerSpecification)
{
    return activeCustomerSpecification.IsSatisfiedBy(this);
}
Thursday, November 22, 2007 8:17:48 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 

Over the past month I have been preparing for my presentation at DevTeach in Vancouver. I am filling in for JP on the topic "Generics they're not just about collections". I am really nervous and one thing that I have been trying to work on is my ability to better communicate the concepts that I have learned and have trapped in my head.

I feel that my past 2 presentations on Windsor Container could have been a lot better if I were able to explain the underlying concepts behind a product like Windsor instead of just taking for granted the audience's knowledge.

After practicing my presentation multiple times in front of my dogs, I am not satisfied with how I am communicating these topics and I feel that writing out my presentation in front of my blog readership and asking for feedback is the next best thing.

If you have any suggestions or if there is anything that is unclear to you based on my explanation of things, please feel free to comment on this post or to send me a email at stevenrockarts@gmail.com.

Using Generics without Collections

Please not that this part of the presentation is based off of JP's original Generics article. How many times have you written a class like the following to check if something is within a range?

public interface IRange
{
    bool Contains(int valueToCheck);
}
 
public class Range : IRange
{
    private readonly int start;
    private readonly int end;
 
    public Range(int start, int end)
    {
        this.start = start;
        this.end = end;
    }
 
    public bool Contains(int valueToCheck)
    {
        return valueToCheck >= start && valueToCheck <= end;
    }
}

If we look at the test for this code we can see that it works great for checking to see if an integer is within a certain range:

[Test]
public void ShouldFindRangeOfIntegers()
{
    IRange range = new Range(10, 20);
 
    Assert.IsTrue(range.Contains(12));
}

The range class works great checking integer ranges but what if we want to check DateTime ranges or our own custom Ranges? Creating a Range class that utilizes generics can help us save typing out a new Range class for every different type that we want to check. Before we jump in and create a Range class that uses generics lets create a couple tests that express our intent:

[Test]
public void ShouldFindRangeOfIntegers()
{
    IRange<int> range = new Range<int>(10, 20);
 
    Assert.IsTrue(range.Contains(12));
}
 
[Test]
public void ShouldFindRangeOfDateTime()
{
    IRange<DateTime> range = new Range<DateTime>(DateTime.Today, DateTime.Today.AddDays(2));
 
    Assert.IsTrue(range.Contains(DateTime.Today.AddDays(1)));
}

The first test shows that we want to check if the integer 12 is in between 10 and 20. The second test shows that we want to check if tomorrow is in between today and two days from now. Now that we have 2 tests that show what we want to accomplish, lets write a generic range class that can handle both of these types. Our first attempt might look something like this:

public interface IRange<T>
{
    bool Contains(T rangeToFind);
}
 
public class Range<T> : IRange<T>
{
    private readonly T start;
    private readonly T end;
 
    public Range(T start, T end)
    {
        this.start = start;
        this.end = end;
    }
 
    public bool Contains(T rangeToFind)
    {
        return rangeToFind >= start && rangeToFind <= end;
    }
}

 As it stands, this code will not compile because the compiler does not know if it can apply the >= and <= signs to any type that may be supplied to our generic range class. For example this may make sense with integers but what does the compiler do with this code:

IRange<Customer> customerRange = new Range<Customer>(customer, anotherCustomer);
customerRange.Contains(someOtherCustomer);

When we try and use the above code we get the following feedback from the compiler:

Error 3 The type 'Generics.Customer' must be convertible to 'System.IComparable<Generics.Customer>' in order to use it as parameter 'T' in the generic type or method 'Generics.Range<T>' C:\presentations\generics\src\test\Generics.Test\RangeTest.cs 24 50 Generics.Test

The IComparable interface contains one method on it called CompareTo() which compares the current instance of an object with another object of the same type and returns an integer based on the result. If the integer is less than 0 the instance is less than the object it is being compared to, if the integer returned is zero the instance is equal to the object it is being compared to and if the integer returned is greater that 0 the instance is greater than the object it is being compared to. Luckily for us most of the value types in the .NET Framework implement IComparable so we can utilize it to compare many different types using our generic Range class.

How can we ensure that any consumer of our generic range class implements the IComparable interface? Generic constraints to the rescue! We simply add the following to our class which specifies that any class that uses our generic Range class must implement the IComparable interface.

public class Range<T> : IRange<T> where T : IComparable

We also need to change the contains method to compare the value to check to the start and end of the range like so:

public bool Contains(T rangeToFind)
{
    return rangeToFind.CompareTo(start) >= 0 && rangeToFind.CompareTo(end) <= 0;
}

Now we can run our tests and discover that both tests pass and our range class now works with integers and date time values.

Thursday, November 22, 2007 8:14:58 AM (GMT Standard Time, UTC+00:00)  #    Comments [1]  | 
Saturday, October 27, 2007

Edmonton Code Camp 2007 has come and gone for another year. Looking back I would say that this year was better than last year. I still have not had the chance to look at all the comment sheets to see what we can do better next year. Thanks to Dave Woods for helping with the website and to everyone at Edmug for helping organize everything. Also thanks to the people that offered to help even though I couldn't find them anything to do.

The UofA venue was a lot better than I expected and we will probably have next years code camp there. Thanks to Mack for suggesting it. The only problem seemed to be with Conference Services telling us that we could tell attendees to park in lot M and then blocking it off for the opera that didn't start until 6pm, those opera singers can be huge divas, I didn't know that they needed a whole parking lot just to accommodate their entourage.

I am most proud of the fact that our community is growing big enough to support something like Edmonton Code Camp. You can bitch all you want about the quality of software developers in the city but if you aren't doing anything to help improve their skills you are part of the problem. As a developer you have to remember where you came from and help people get to where they want to go.

It is also awesome to see that developers from Edmonton and Calgary are getting to know each other a lot better. I remember driving down to Calgary about a year and a half ago and meeting people for the first time. Mo Khan has some good thoughts on the growing community.

I'll be posting my code from my Introduction to Windsor Container presentation on the Edmonton Code Camp website in the next couple of days. In the meantime you can check out the code from the other presentations at the Edmonton Code Camp website on the source code page.



Saturday, October 27, 2007 12:11:43 AM (GMT Standard Time, UTC+00:00)  #    Comments [1]  | 
Tuesday, October 23, 2007

Instead of answering questions about specific technologies in technology specific forums, try your hand at the universal language of Refactoring at a new site from Marc-André Cournoyer named Refactor My Code. Cool stuff!

Tuesday, October 23, 2007 6:48:53 AM (GMT Standard Time, UTC+00:00)  #    Comments [1]  | 
Thursday, October 18, 2007

public static void ThinkBeforeYouUseADataSet(this DataSet dataSet)

{

throw new NotSupportedException("Use a rich domain instead");

}

Thursday, October 18, 2007 8:02:14 PM (GMT Standard Time, UTC+00:00)  #    Comments [5]  | 
Friday, October 12, 2007

Dog Trick or Cat Treat

I finally got something published but it is not technical by any means. Two pictures of my dog Daisy have been published in the book Dog Trick or Cat Treat: Pets Dress Up For Halloween by ECW Press.

Friday, October 12, 2007 2:18:03 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Monday, October 01, 2007

When you have to do 5 timesheets to let the business client know your "status".

I feel bad for the poor person on the business team that has to go through all the timesheets that are submitted, I would guess they aren't even reading the timesheets and it is all a big conspiracy to waste hard drive space. 

Will they be confused if I add a task for entering time instead of developing?

How come people don't realize that a business client can get more from a 5 minute standup than they can from reading timesheets?

Monday, October 01, 2007 10:29:56 PM (GMT Standard Time, UTC+00:00)  #    Comments [3]  | 
Sunday, September 30, 2007