Adopting the design kung fu of TDD was one of the better practices I've picked up as a developer in the short time that I've been one. I build out the behavior of my system with stable, solid code and maximum reward. Due to its method of design (unit testing) I've also learned other important skills in creating loosely coupled code.
The one style of programming that goes nearly hand-in-hand with TDD is programming against interfaces. This is a decades old approach. What I find to be the most enjoyable behavior of interfaces is the fact that it's breaking the client code from the implementing code. You're trying to loosen up your coupling. I used to think it was insulting to create an interface with any less than one implementing class. I was missing the point. I forget where I read it but I saw someone who summarized it wonderfully, “Creating interfaces is not to the benefit of the implementing code but rather to the client.” Points to anyone who can help me find the original source.
TDD becomes a natural litmus test for discovering when I should introduce an interface. You get what's commonly called “test friction”. Friction is simply noise in your tests that has nothing to do with the component you're testing. It causes them to easily crash or be brittle, and makes it hard to test overall. Anything that turns your tests into massive scripts or breaks often can be considered test friction. Abstracting out the friction into interfaces is key to keeping the tests focused and your classes loosely coupled.
At first you'll be surprised at how many interfaces you end up creating but then it eventually feels right. The interfaces were always there between your objects, you're just formalizing it. What's wrong with that?
Well, actually, maybe a couple of things. I do get annoyed with the ceremony of interfaces. First I need to make the compiler happy. I may have a class that perfectly conforms to the expected method signatures of an interface but I can't use that in place of that interface; it's not the right type.
So then I need to create the interface every time I need to abstract something(which is often enough). Being an obsessive, compulsive coder, I create a new file for it (although, from time to time, I may embed them with their default implementation). I'm introducing noise into the project by having more files and more types to wade through in the auto-complete. Not ideal.
Recently I've looked at dynamically typed languages and felt a secret jealousy. They don't have interfaces. It's just good old fashioned duck typing. It's just messages being passed between objects. Why can't I do that? I don't need an interface to pass messages between objects. The object either accepts those messages or it doesn't. No need to paint myself into a corner. Testing becomes easier. I create objects on the fly, mock/stub/fake them up with hardly the effort it takes in C#. No extra interfaces necessary.
I guess the root of this may be that I'm finding the world of statically typed languages to be confining. It's been a feeling I've had for at least the last half of year or so. I've had a hard time articulating what it is that bothers me but I'm seeing it at as these things that I'm being forced into obeying by the compiler. Statically typed languages will certainly help you to avoid simple issues in your types but it can't guarantee you won't run into runtime issues anyways. Who can say they've never encountered a InvalidCastException? NullReferenceException? Statically typed languages are still exposed to the same problems that dynamically typed languages are. Compilation is providing compile-time debugging of your types. But if you're already writing unit tests, which undoubtedly test the interfaces of your objects, does that negate the advantage of compile-time debugging?
Most accounts that I've read of developers sharing a sentiment like my own usually end up embracing dynamically typed languages. Some even switch specialization and jump ship. I don't know what to do yet. I guess I have to go and find out for myself.
There' an awesome alt.net podcast that really speaks to where my head is at. It's an interview with Scott Bellware so if you dig his contributions to the community then you'll certainly enjoy this. I strongly recommend you give it a listen.
OOP in Ruby with Scott BellwareAOP