.NET

Saying No To Properties

Properties are rarely necessary and, in many cases, they make your classes hard to extend and difficult to debug. And they make code much larger and more complex than it needs to be.

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:

(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:

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:

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:

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.

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.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!