Sunday, September 18, 2005

Dependencies

One of the most important jobs a programmer has is to manage dependencies. In fact, many of the Code Smells that we learn to avoid have their roots in the removal of unnecessary dependencies.

Especially evil are implicit dependencies (i.e., dependencies that are not easily noticed/not explicitly written). For example, once and only once is about the removal of implicit dependencies. Why? Because all instances of duplicated code depend on each other. If you have the following lines

int i = 0;
for( ; i < 10; ++i )
{
...
}
i--;

and you decide that the last i-- is a bug, then you have to remove the last i-- in every instance of the duplicated code - they are all dependent on one another. What's worse is that there is no semantic information associated with this code. It could be that one instance of the duplicated code actually precedes something slightly different so that the i-- is actually needed. Because there is no semantic information tieing all the pieces together, there is no way to know which i-- should stay and which should go.

Magic numbers, being another form of code duplication, have exactly the same problems; however, the problems with lack of semantic information is exacerbated with magic numbers. In the code above, there is no knowledge of what the number 10 represents. When you realize that it should be 15 do you change all the instances of 10 to 15? If not, how do you know which ones to change and which ones not to?

Dave Chelimsky discusses how to Manage Dependencies Not Aesthetics in his blog. He uses the following example:

display.print( person.getAddress().getZipCode() );

and then compares it with

display.print( person.getZipCode() );

and

person.printZipCode(display);

Which one has its dependencies managed correctly? Let's examine where the dependencies are. In example 1, there is a dependency from the main code to display, person, address, and zip code. There is also a dependency between display and zip code. Even though zip code is just a string, there is a logical dependency between the concept of a zip code and display.
In example 2, there is a dependency from the main code to display, person, and zip code. The dependency on address has been eliminated. There is still a logical dependency from display to zip code. In example 3, there is a dependency from the main code to both person and display, the dependency to zip code has been removed (even though the function still says zip code, the zip code is not seen or used in the main code, so there is no dependency). There is an additional dependency between a person and a display object.

Now, elimination of dependencies is not the goal, only the effective management of dependencies; however, it is much easier to manage a dependency that doesn't exist, just like it is much easier to maintain code that doesn't exist. I think we can quickly eliminate example 1 as being the best because it has no advantages over example 2 and an additional dependency. Example 2 does have an additional dependency over Example 3, but there are some trade-offs involved. With example 2, if the zip code changes, then the display class must also change. With example 3, if you want to write to something other than a display, then the person class must change. So, the question becomes which is more likely to change. Obviously this will change on a design by design basis.

Interestingly enough, the comments section of David's blog dives off into a discussion of formatters and visitors and how to isolate the change, but each introduces more dependencies in an effort to find the best dependency combination that is least likely to change. In this case, I might stick to the TDD principle of doing the simplest thing possible and then refactoring when we actually see the change, at that time you can make a more informed decision about what needs to be changed and how the change needs to occur.

No comments: