http://www.codinghelmet.com/  

Wear a helmet. Even when coding.

howto > internal-consistency-of-objects

How to Ensure Internal Consistency of Mutable and Immutable Objects
by Zoran Horvat @zoranh75

Motivation

Internal consistency is fundamental requirement for all objects. Without consistency guarantee, all clients of an object would have to defend from unpredictable circumstances whenever they try to do anything. In consistent world, client calls an operation on an object like this:

obj.DoSomethingForMe();

But if client is uncertain about object's status, it must plug all the holes:

if (obj.AreYouFine())
    obj.DoSomethingForMe();
else
    this.GoBellyUp();

This piece of code has two fundamental flaws. First, it tests object's status explicitly - something that heavily relies on programmer's consciousness. Poorly informed coder, or just a sleepy one, could easily forget to test the object before accessing it. Second flaw is that the code has a negative branch which is activated when subordinate object is not fine. This leaves the ultimate consumer of this code in a very unpleasant situation because there is no certainty that the operation is going to succeed at any given time. Even worse, we cannot say whether the desired operation is going to be done, or is it going to explode back into our face.

Bottom line is that any object we reference must be fine at all times. Object with incomplete content, missing references, mismatched internal values, etc. is inconceivable.

Enforcing Consistency in Objects

We can generally divide all objects by their ability to change own internal state. Those objects that do not change after they have been initialized are referred to as immutable objects. On the opposite end there are mutable objects, which expose operations that change their internal state.These two kinds of objects are kept consistent in different ways.

Consistency in Immutable Objects

Example of immutable objects are instances of the System.String class. Once a string is initialized, it doesn't allow changes to its underlying buffer. Any operation that sounds like changing the string actually instantiates another string object and returns it as a result. For example, Insert method lets us insert one string into another at specific position. It does so by creating another string with appropriate content:

string a = "sing";
string b = a.Insert("ometh", 1);
// b: "something"

Constructor is the only place where internal state of an immutable object can be set. The question how to ensure consistency of such an object then points in constructor's direction. Constructor is in charge to validate its arguments and to make sure that object is properly and fully initialized from them.

As a practical demonstration, consider a class which represents an item in a purchase receipt:

public class PurchaseItem
{

    public string UPC { get; private set; }

    public decimal UnitCost { get; private set; }

    public int Quantity { get; private set; }

    public decimal TotalCost
    {
        get
        {
            return this.UnitCost * this.Quantity;
        }
    }

    public PurchaseItem(string upc, decimal unitCost, int quantity)
    {

        if (string.IsNullOrEmpty(upc)
            || unitCost <= 0.0M
            || quantity <= 0)
            throw new ArgumentException();

        this.UPC = upc;
        this.UnitCost = unitCost;
        this.Quantity = quantity;

    }

}

This class is immutable. Product code (UPC), unit cost and quantity purchased cannot be changed after initialization. Now suppose that we have a requirement to let the user change quantity before the receipt is printed. That can easily be done in brigh tradition of immutable objects - by creating a new object from the existing one:

public class PurchaseItem
{

    ...

    public PurchaseItem ChangeQuantity(int quantity)
    {
         return new PurchaseItem(this.UPC, this.UnitCost, quantity);
    }

}

Observe that in this method we have not checked whether quantity passed to the method is positive or not. That validation is performed by the constructor when new object is initialized. Current object really doesn't care about validity of this argument. It is the new object responsible to make itself valid or to fail to initialize if consistency rules have been violated.

Consistency in Mutable Objects

Objects that are allowed to change after initialization still have to be consistent. This means that their constructor is responsible for validation none the less. But after the initialization, client is free to call some operations that modify this established consistent state. Those operations must ensure that consistency is preserved over state changes.

Take a purchase receipt as an example. Receipt class will aggregate a set of purchased items. Its responsibilities will be in the domain of manipulating the items and providing basic aggregation services, such as summing up item costs. Here is the first look at the Receipt class:

public class Receipt
{

    private IList<PurchaseItem> Items;

    public Receipt()
    {
        this.Items = new List<PurchaseItem>();
    }

    public void AddItem(PurchaseItem item)
    {

        if (item == null)
            throw new ArgumentNullException("item");

        AddItemInternal(item);

    }

    public PurchaseItem RemoveItem(string upc)
    {

        if (string.IsNullOrEmpty(upc))
            throw new ArgumentException();

        PurchaseItem item =
            this
            .Items
            .Where(i => i.UPC == upc)
            .Single(); // may throw exception

        this.Items.Remove(item);

        return item;

    }

    public decimal TotalCost
    {
        get
        {
            return this.Items.Sum(item => item.TotalCost);
        }
    }

    private void AddItemInternal(PurchaseItem item)
    {
        if (ContainsUpc(item.UPC))
            AddQuantityToItem(item.UPC, item.Quantity);
        else
            AddNewItem(item);
    }

    private bool ContainsUpc(string upc)
    {
        return this.Items.Any(item => item.UPC == upc);
    }

    private void AddQuantityToItem(string upc, int quantity)
    {
        PurchaseItem item = RemoveItem(upc);
        item = item.ChangeQuantity(item.Quantity + quantity);
        AddNewItem(item);
    }

    private void AddNewItem(PurchaseItem item)
    {
        this.Items.Add(item);
    }

}

Observe how we had to make many tests before any operation was done which alters internal state of the object. Actually, most of the Receipt class code is dedicated to preserving internal consistency of the object. That goal is far from trivial to achieve. Therefore, before deciding to let the object be mutable, consider cost of that decision. In many cases it is not worth the price. Instead of trying to predict all issues that may arise while changing internal state of the object, it might be quite sufficient to let the final state be specified through the constructor and then seal the object for changes, i.e. make it immutable.

Conclusion

In this article we have demonstrated two ways in which objects cope with consistency issues. One is simpler and it is fully implemented in the constructor. The other is more complicated and it copes with state transformations.

In practice, it is easier to write immutable objects because with them we do not have to consider all possible state changes. That saves many lines of code and reduces chance that we have forgot some viable scenario. Only if it is absolutely necessary shall we design a mutable class. And in that case, we must ensure that all valid transitions are covered with logic which protects object from corruption.

See also:

Published: Mar 21, 2014

ZORAN HORVAT

Zoran is software architect dedicated to clean design and CTO in a growing software company. Since 2014 Zoran is an author at Pluralsight where he is preparing a series of courses on object-oriented and functional design, design patterns, writing unit and integration tests and applying methods to improve code design and long-term maintainability.

Follow him on Twitter @zoranh75 to receive updates and links to new articles.

Watch Zoran's video courses at pluralsight.com (requires registration):

Making Your C# Code More Object-Oriented

This course will help leverage your conceptual understanding to produce proper object-oriented code, where objects will completely replace procedural code for the sake of flexibility and maintainability. More...

Advanced Defensive Programming Techniques

This course will lead you step by step through the process of developing defensive design practices, which can substitute common defensive coding, for the better of software design and implementation. More...

Tactical Design Patterns in .NET: Creating Objects

This course sheds light on issues that arise when implementing creational design patterns and then provides practical solutions that will make our code easier to write and more stable when running. More...

Tactical Design Patterns in .NET: Managing Responsibilities

Applying a design pattern to a real-world problem is not as straight-forward as literature implicitly tells us. It is a more engaged process. This course gives an insight to tactical decisions we need to make when applying design patterns that have to do with separating and implementing class responsibilities. More...

Tactical Design Patterns in .NET: Control Flow

Improve your skills in writing simpler and safer code by applying coding practices and design patterns that are affecting control flow. More...

Writing Highly Maintainable Unit Tests

This course will teach you how to develop maintainable and sustainable tests as your production code grows and develops. More...

Improving Testability Through Design

This course tackles the issues of designing a complex application so that it can be covered with high quality tests. More...

Share this article

webmasters