Tightly coupled code is hard to test. And hard to test mean hard to extend, hard to modify and hard to read. There are a number of ways you can couple your code. I’ll list some of them, and if you find you are using them, try finding other ways.
For the sake of simplicity, whenever I write interfaces, you should read out loud “interfaces or abstract classes”.
1. Using concrete classes
If your code accepts concrete classes in and returns concrete classes, you have a warning sign right there. You should only work with interfaces. Instead of returning a List<string> you should return a IList<string> , or a ICollection<string> That way, you are not commiting yourself to using a List<string>, when a SortedList<string>.
might be better tomorrow.
2. Using static classes
An excuse I hear a lot to why static classes are used is that the class needs to be a singleton. First of: singletons are evil!
Seriously – they are bad. In your code, you should concentrate on what the object does, and not how it is accessed. Let the client worry about the “single state” stuff, which is not hard to solve anyway… The only time statics might be excusable are when they are state-less utility classes. And not even then should you make a habit out of it.
3. Tell, don’t ask
Consider this code:
dog.getBody().getTail().wag();
(Shameless rip from Uncle Bob)
If a lot of your code looks like this, you have a problem. Your classes are not encapsulating the user very well. A better way would be:
dog.expressHappiness();
In the first code snipper, you are forcing the client to know a lot about the inner workings of your classes, and that in turn forces you to keep the inner workings just like that.
4. Creating objects in classes
If your code creates objects in it’s constructor or in it’s methods, you have commited and bound that class to another class. This is bad. Instead, let the client make the choices for you. This means that the client kan change the behaviour by giving your class something else.
For example the StreamWriter is a powerful type because it let’s you, the client, give it any stream to work with, no matter what the back end of that stream is. You can use it to write to memory, to a compressed file, or to the network. It’s all the same to the StreamWriter!
That’s all for today.
These are all great points! It is strange how hard it can be to get people to accept them as a base for good code. One thing I would like to add, although it is more or less covered by the others together, is the Law of Demeter (http://c2.com/cgi/wiki?LawOfDemeter). A method should only directly call other methods in it’s class, methods on it’s own class’ fields or methods on objects it was given as parameters. Never call methods on the object that is returned by these methods though!
Like Andrés says above, how do you know how a dog expresses happiness? I can assure you that when my dog expresses happiness she does a lot more then wag her tail.
Left by Chris Hedgate on February 1st, 2006
There’s also a ‘temporal coupling’ dimension – ponder the difference between supplying references to a method call and supplying them at constructor time. Are your sure that reference will always be the same for your method calls during the lifespan of the object?
Left by Stefan on February 1st, 2006