Unfortunately, this is the case with virtually every contract that relies on a property. The class contract becomes unenforceable. The only way to fix the problem is to completely hide the type of the Value object, but there's no way to do that if we expose the object to the outside world. Value must be be completely encapsulated inside the object. (It should be private with no public accessor or mutator.)
To drive the point home, having a Value without a currency isn't of much use. If you're thinking in terms of properties, however, it's tempting to do something like the following to add the notion of currency:
class Money
{
public decimal Value { get; set;}
public CurrencyCode Currency { get; private set;}
//...
}
(I'm not permitting modifications to the currency through the property because you need to pass arguments like a target currency to do that).
The amount of work this simple change requires is simply unacceptable. For example, your original code might calculate a simple sum: total.Value += item.Value; Once we introduce the Currency property, however, we need to change that simple statement to something like the following:
total.Value += item.Value *
CurrencyCoverter.getExchangeRate(
total.Currency, lineItem.cost.Currency);
The new code is ugly, but more significantly, we have to make a similar change everywhere we add together two units of Money. That could add up to many hundreds, or even thousands, of changes scattered throughout the code, and we'll almost certainly miss a few, thereby introducing many new bugs. Automatic refactoring won't do this work for us.
You could argue that you can do the currency conversion in the Value setter, which is definitely a move in the right direction. I'd wager that a better approach is to give up on the properties entirely and completely hide the way that the value is represented from the outside world.
Here's what I'd do. Let's first rewrite the simplistic example to hide implementation:
class Money
{ private readonly float value;
public Money ( float value )
{ this.value = value;
}
public Money plus( Money operand )
{ return new Money(value + operand.value);
}
}
I can now update the total like this:
Money total; Money item; //... total = total.plus( item );
Of course, I could use operator overloading to make it look like this:
total += item;
but I'll keep the example simple.
The first thing to notice is that full encapsulation has made the client code simpler (and shorter), and that alone is a good reason to use this approach. You will (hopefully) use Money more often than you define it, so encapsulation can result in considerable reduction in code size, along with the concomitant reduction in development time and debugging cost. It takes a bit longer to write the class, but it takes a lot less time to write the code that uses the class.
Let's see how this approach holds up to change. What if I want to change the internal representation of value to decimal? Here's the new class:
class Money
{ private readonly decimal value;
public Money ( decimal value )
{ this.value = value;
}
// For backwards compatibility
public Money ( float value ) : this( (decimal)value )
public Money plus( Money operand )
{ return new Money(value + operand.value);
}
}
Note that the code to compute the sum hasn't changed at all. It still looks like this: total = total.plus( item ); That point is the essential one. I can change the implementation without changing any of the code that uses the original implementation.
To drive in my point, let's add the notion of currency:
class Money
{
private readonly decimal value;
private readonly CurrencyCode currency;
public Money ( decimal value, CurrencyCode currency = CurrencyCode.USD )
{ this.value = value;
this.currency = currency;
}
//...
public Money plus( Money operand )
{
decimal newValue == value +
(operand.value *
CurrencyCoverter.getExchangeRate(currency, operand.currency));
return new Money(newValue, currency );
}
}
The code that does the sum has still not changed: total = total.plus( item );
I hope you're getting the idea. By fully encapsulating the implementation of Money eliminating the properties entirely I end up with an implementation that I can change radically without changing any of the code that uses the Money class.
This sort of flexibility is much more difficult to achieve if you use properties.
What about testability? The fact that the client code isn't changing gives us a test advantage. In general, it's a bad idea to change both your test and the class that you're testing at the same time. If you do, you're never sure whether a bug is in the test or in the class. In a regression-test environment, you don't really want to change your test code at all. (That's not to say that a test doesn't have a lifetime. In an Agile world, it's possible for a class to have to change enough that the old tests simply aren't useful any more, in which case they should be discarded.) With properties, though, you'll certainly have to rewrite your tests every time a property changes type (or you add or remove one).
Properties also add unnecessary coupling relationships between classes, which also hurts testability. For example, if you use a property, you need to test not only the object you're interested in, but also all the objects whose properties are accessed and the interactions between the entire system of interlocking objects. That makes it very difficult to write a unit test or more accurately, your smallest "unit" becomes a system of interrelated classes rather than a single class.



