Saturday, September 22, 2012

Refactoring to Maybe pattern


The NullReferenceException is the most commonly occurring bugs in any programming language.
Passing nulls from a method leaves the responsibility to check the validity of the response object to the callee object/ method which leaves the space of null reference exceptions. For example in the code given below, the method which calls the repository’s GetById method needs to make sure that the Customer object is a valid one before making the assertions.
[TestMethod]
public void GetCustomerReturnsCustomerWithIDFromDB()
{
    var repository = new CustomerRepository(_dbContext);
    const int customerId = 5;
    var customer = repository.GetById(customerId);
    if(customer != null) Assert.AreEqual(customer.Id, 5);
    Assert.Inconclusive(string.Format("Customer with Id {0} was not found in the context", 5));
}
public Customer GetById(int id)
{
    return _context.Customers.FirstOrDefault(x => x.Id == id);
}
Refactoring the code to a maybe pattern implementation will make the repository define the contract of GetById to return a Maybe object which the callee always expects.
public interface Maybe<out T>
{
    bool HasValue { get; }
    T Value { get; }
}
public class Some : Maybe where T : class
{
    private T _value;

    public Some(T value)
    {
        _value = value;
    }

    public bool HasValue { get { return true; } }
    public T Value { get { return _value; } }
}
public class None : Maybe where T : class
{
    public bool HasValue { get { return false; } }
    public T Value { get { throw new MaybeException();} }
}
The GetById method signature now changes to
public Maybe<Customer> GetById(int id)
{
    var customer = _context.Customers.FirstOrDefault(x => x.Id == id);
    return customer == null ? new None<Customer>() : new Some<Customer>(customer) as Maybe<Customer>;
}

No comments: