Sunday, April 21, 2013

Object Oriented State

It is very easy to lull ourselves into the the feeling that we are writing object oriented code, especially in languages like Java and C#, without giving much thought to what that really means. The idea behind object oriented programming is to combine behavior with state—or rather, encapsulate state within an object and expose behavior. Juxtapose this with procedural languages, like C, where state and behavior are separated. This "feeling" that we are writing object oriented code usually seems to stem from the idea that we are wrapping our code in classes.

Please, do not get me wrong, I am not bashing procedural code. In fact, I believe that procedural code, used correctly, can be easier to understand and reason through. In fact, it is used pretty heavily in object oriented programming to implement procedural pieces. That is probably why we tend to write so much procedural code, even in object oriented languages. Unfortunately, I think too many people currently view their work as object oriented programming if they are using classes. I think a distinction needs to be made because we should reason about our code more often. While I will save that argument for another post, what I will cover is a few examples of how we can make our code more object oriented, if that is our end goal.

Anemic

Without digging too much into domain driven design (DDD), command query responsibility segregation (CQRS), or other topics, I want to explore some code and compare the differences and consequences of the design. Let's start with a shopping cart that can hold products. We'll use a dictionary with the product as the key and the quantity as the value.

public class Product
{
    public decimal Price { get; set; }
}

public class ShoppingCart
{
    public Dictionary<Product, int> Items { get; set; }
}

If we stick to this (anemic) "object oriented" design and, say, try to sum the items in a shopping cart, we are going to need something like this (using LINQ to simplify the code):

public class ShoppingCartController : Controller
{
    ...
    public ActionResult GetCartSubTotal()
    {
        var cart = cartRepository.GetCartForUser();  // Use your imagination here.
        var total = cart.Items.Select(x => x.Key.Price * x.Value).Sum();
        return PartialView(total);
    }
}

Which doesn't seem too bad, I suppose. But now we need the sub total in another portion of the application. After a while, we have a few places where this code is repeated. So, we decide, simply, to refactor the code and expose a method that returns the subtotal.

A little better

public class ShoppingCart
{
    public Dictionary<Product, int> Items { get; set; }

    public decimal GetSubTotal()
    {
        return Items.Select(x => x.Key.Price * x.Value).Sum();
    }
}

It seems this is where most developers stop. There is more we can do, though, to clean up our design. If we are truly worried about encapsulation, why, oh why, can the rest of the world manipulate our dictionary of items? What happens if we decide to create a new class that holds a reference to a product and the current quantity, thus using a List instead? Now we have to go back through the application and clean it up. But, why do we even go through that in the first place? What if, instead, we all agree that (unless absolutely necessary), we will not use public properties? We could transform our cart into something like:

public class ShoppingCart
{
    private readonly Dictionary<Product, int> _items = new Dictionary<Product, int>();

    public decimal GetSubTotal() { ... }
    public int AddProductToCart (Product product, int quantity) { ... }
    public void RemoveProductFromCart (Product product) { ... }
    public void RemoveProductFromCart (Product product, int quantity) { ... }
}

Thus, we have successfully pulled all of the algorithms to perform the different functions on a shopping cart into one place. We can very easily test this code, and we can refactor it without touching the rest of our application. This allows us to follow "tell, don't ask," where we tell our shopping cart to add an item instead of asking it for the underlying items so we can, via external code, add an item or update the quantity.

Exposure

But, I bet you're wondering, how is the rest of the world supposed to know about the items we have in this cart if we don't expose the collection of items? Good question! We can create a read only view model, or a snapshot, of the underlying data that is safe for the rest of the world to use.

public class CartEntry
{
    public string ProductName { get; private set; }
    public decimal Price { get; private set; }
    public int Quantity { get; private set; }

    public CartEntry(string productName, decimal price, int quantity)
    {
        ProductName = productName;
        Price = price;
        Quantity = quantity;
    }
}

public class CartView
{
    public IEnumerable<CartEntry> Items { get; private set; }
    public decimal SubTotal { get; private set; }

    public CartView (IEnumerable<CartEntry> items, decimal subTotal)
    {
        Items = items;
        SubTotal = subTotal;
    }
}

public class ShoppingCart
{
    private readonly Dictionary<Product, int> _items = new Dictionary<Product, int>();

    public CartView GetView()
    {
        var items = _items.Select(x => new CartEntry(x.Key.Name, x.Key.Price, x.Value)).ToList();
        return new CartView(items, GetSubTotal());
    }
}

It may seem like a lot more code, but when you see how much repeated logic and code you pull into one place, and how much more testable this is, it will be worth it. Of course, there are always exceptions and use cases when something like this doesn't make sense. Of course, when those use cases are found, it is usually because one has reasoned about their code and has a good reason not to put in the little bit of effort to truly follow an object oriented approach.

No comments:

Post a Comment