Encapsulation and DRY Go Hand in Hand

Avoiding the Primitive Obsession anti-pattern goes hand in hand as well, but doesn’t have quite the same ring to it, so I didn’t try to fit it into the title.

In fact, Avoiding Primitive Obsession is a good place to start. Take for example the following code sample:

public class Product
{
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int QuantityInStock { get; set; }
}

Primitive Obsession is a bit hyperbolically named, since it is used to describe code which is “obsessed” with primitives, when it might be more accurate to describe the code that chooses to use language primitives by default, beyond the point that a custom type would have been more suitable.  It has nothing to do with cavemen.

The code sample above uses a System.Decimal numeric type (for those of you in the .NET world) to represent a product’s price.  This might seem sensible at first, until you consider that a decimal can represent values ranging from negative 79,228,162,514,264,337,593,543,950,335 to  positive 79,228,162,514,264,337,593,543,950,335, but surely these are not reasonable values for product prices.

Similarly with System.Int32 that is used to describe the QuantityInStock.  The integer can represent values from about negative 2 billion to positive 2 billion, unlikely for product quantities.

It might seem easy at first glance to simply avoid setting these properties to outrageous values, and in many cases this will indeed be the best approach.  But consider that these value might be set from multiple places in code.  Consider that there might be a business requirement that no product ever be priced greater than some amount well in excess of any actual products carried by the business, say $10,000, as a fraud prevention measure.  Consider that Price values might get set from a multitude of places throughout a very large project.  Now consider the following code snippet:

public class Price
{
    private readonly decimal _price;

    public Price(decimal p)
    {
        if (p < 0)
            throw new ArgumentException("Price must not be less than zero.");
        if (p > 10000)
            throw new ArgumentException("Price exceeds the maximum allowed value.");
        if (p % 0.01m > 0)
            throw new ArgumentException("Price must not contain fractional cents.");

        _price = p;
    }

    public override bool Equals(object other)
    {
        var otherPrice = other as Price;
        if (otherPrice == null)
            return false;
        return otherPrice._price == _price;

    }

    public override int GetHashCode()
    {
        return _price.GetHashCode();
    }

    public static explicit operator decimal(Price price)
    {
        return price._price;
    }
}

If you look at the constructor, you’ll see that it is impossible to initialize an instance of the Price class without an exception being thrown.  This might not seem like a big deal, but many applications let these small bits of validation details leak out all through the remainder of the application code, often repeatedly, and sometimes even inconsistently.

But with this Price implementation, we have ensured that the knowledge of exactly what constitutes a valid Price within our domain is completely Encapsulated in this one place.

An additional possible benefit is that it is now impossible to inadvertently mix Price decimal values with any other decimal values throughout your project.  Admittedly, I have not seem many bugs in the wild caused by such basic numeric mix-ups, but I have however worked with a number of external services (Endeca, AWS Kinesis) which tend to inundate you with a variety of human-unreadable strings which you must not mix up.  Sometimes even the names of the string values (“Shard Iterator, Sequence Number,” etc.) were a bit difficult to keep straight following a night of poor sleep.  By encapsulating such values within their own custom types, the compiler itself will ensure that you do not mix the disparate types.

Still another benefit to this approach is its expressiveness.  The code now contains a Price type which describes everything about a Price in your particular domain.  Using the primitive approach, one has to read through all of the usages of the primitive to deduce the rules surrounding its usage.  Eventually the entirety of rules by which your system operates become impossible to fully deduce, and your team might start to rely heavily on tribal knowledge, when instead the code could itself enforce the correctness of the data which it manipulates.

Naturally given the ~1/2 page of code necessary to create the custom Price type, you probably will not want to create custom types for every single primitive in your system.  Rather it is best to be judicious, and only apply the effort once you have identified particular values in your domain which are particularly susceptible to either large scale duplication of validation logic, or modest scale duplication where the validation itself is slightly more complex.

If you want nearly zero effort type decorations, I’d highly suggest checking out F#.

Alas, I feel that I have reached the length limit for a reasonable blog post, so I will leave off here to resume in a future post, even though I have not even begun to discuss DRY and have only lightly covered encapsulation.  Perhaps I can roll all of this into a single Grand Unified Programming Principle that covers everything.

For now, let me end with the thought, “What if these same principles were applied to objects which represent almost all of the main concepts in your system…?”

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s