ArticleS. BobKoss.
DejaVu [add child]

Deja Vu: Inheritance Bytes.



I’m sure that you’ve heard the saying, “Fool me once, shame on you. Fool me twice, I must be too stupid to live,” or something like that. The point is that we should learn from our mistakes and try not to repeat them.

Back in the early 90’s I was working with a client developing a Windows application in C++. This was the era of Windows 3.1, dBase was king of the databases, Lotus 1-2-3 was the spreadsheet of choice, and laptop computers resembled suitcases – in other words, the good old days. We needed to interact with a database from our C++ application and there weren’t many databases that provided a C++ API. The database that we chose to use required us to inherit our domain classes from one of the classes in the library API and to override some methods. I don’t remember the details (I lost a lot of brain cells in the last 15 years) so for the sake of having something concrete to talk about, let’s say the library class that we were supposed to inherit from was named PersistentObject and we had to override the methods load() and save(). If my domain class was Patient, the design looked like this:







I didn’t understand the importance of dependency management in those days, the Dependency Inversion Principle was just a twinkle in Uncle Bob’s eye, and I didn’t realize the dangers lurking in inheriting from PersistentObject. This must be a good design, I reasoned, because the sample code that came with the persistent library and the tutorials all showed this same structure. Surely I could believe whatever a vendor told me. Furthermore, it worked. Nothing validates a design better than the fact that it actually works, and I was saving and retrieving Patients, LabTests[?], and all kinds of things I can’t remember because of that brain cell thing.

This architecture continued to work, right up until the decision was made to replace the persistence library with a different one from a different vendor. Of course the new persistence mechanism didn’t work the same way and every class that inherited from PersistentObject had to be gutted and fixed-up to work with the new way of doing persistence. That was almost every class in the application. Furthermore, when adjusting the persistence mechanism, many bugs were introduced because the pieces of the class that knew about persistence was tangled together with other pieces of the class that knew about business rules – a gross violation of the Single Responsibility Principle (which I also didn’t know about 15 years ago).

I think nearly every design can be improved in hindsight, and this one was no different. If I had separated the concepts of business logic from persistence, the transition to the new database would have been much easier. Even if we didn’t have to change databases, separating these two concepts would have made the code easier to understand and hence maintenance (read that as bug fixes) would have been easier.

Fast-forward to present day where I’m fatter, balder, and possibly just a little bit smarter.

I was recently teaching a class in Test Driven Development, making statements to the effect that I like to run my unit tests every few minutes, when I noticed strange looks on the faces of the students. Some people were outright laughing (at me?). What was so funny? Quick check of my nose to be sure no boogers were hanging there, checked that I was zipped up and didn’t have any wet spots, and I had to ask – what’s so funny? The response was along the lines that what I was teaching made a lot of sense but it wasn’t going to work in their development environment.


There are certainly languages and environments where it is difficult to do TDD, but that wouldn’t be the way that I would bet since I knew that these folks were using Java and WSAD(Eclipse). I inquired as to why they felt that they couldn’t do TDD the way I was demonstrating (write a piece of a test, write the code to get it to compile but still fail, make the test pass, and then make it refrigerator code).

They claimed that it took several minutes to run their tests. Running tests that take several minutes to run every few minutes would mean not a lot of programming would get done. Okay, that’s fair. Long running tests are a problem so I tried to determine what was taking so long. As it turned out, their application framework had to be initialized before each run and that alone took several minutes on top of the time it took to build the application and to deploy it for testing. The solution was obvious – all I had to do was to show them how to break the dependency on their framework. We spent some time examining their code and I sketched out some UML to help me get an overview of their architecture. I could not believe my eyes when I saw that their architecture looked exactly the same as the UML shown above (from 15 years ago) where every class in the system had to extend a class in their homegrown framework. Just replace PersistentObject with BusinessObject and we have déjà vu.







InsuranceClaimTest is the unit test that is revealing the design problems. It of course must depend upon InsuranceClaim in order to test it, but notice how the unit test is also coupled to what I’m calling BigUglyAndSlow due to the transitive dependency through BusinessObject[?]. It is this transitive dependency that is preventing my clients from practicing TDD.

I’m probably being too hard on BigUglyAndSlow, but that’s just my way. The truth is that the framework does a lot of work for us. It handles persistence, lazy initialization, transactions, serializing to the user interface tier, etc., so clearly its use cannot be and should not be avoided. It took years to develop and it relieves programmers from having to write these aspects of their application. But, in order to use it, programmers have to extend BusinessObject and that is forcing them to write legacy code (defined as code without tests) because testing is more painful than not testing.

To persist an object, the class has to inherit from BusinessObject. Or does it? Something has to inherit from BusinessObject[?], but it doesn’t have to be the domain objects. What if we used the Adapter Pattern and had the adapter extend BusinessObject and adapt the interface of the domain objects to what the persistence mechanism required? The design looks like this:








When save() is called on InsuranceClaimAdapter, it knows to call the getters of InsuranceClaim. InsuranceClaimAdapter knows how to persist an InsuranceClaim but InsuranceClaim now doesn’t know anything about the concept of persistence. Notice how the unit test now only depends upon InsuranceClaim[?] and no longer have any dependency upon the framework.

I seem to recall way back in my career as a C programmer that adding an extra level of indirection could solve all problems (how in the world did the brain cells forming the neural network for that memory survive all these years and yet I can’t seem to remember my current cell phone number? ). In this case, the extra indirection was the application of the Adapter Pattern which allowed us to do TDD in an environment where it was previously deemed to be more trouble than it was worth.










!commentForm

 Wed, 11 Jan 2006 16:37:17, Denis Haskin, comment on site's RSS feed, not this article...
(I can't find any way to get in touch with anyone on this site, so I'll just post this here...)

Looks like the RSS feed for this site was recently enhanced to include a post's content, which is /great/, except... the content is being sent as fitnesse markup, not as rendered HTML. So it looks mighty ugly in an RSS aggregator. Seems to me you probably want to send HTML instead.
 Wed, 11 Jan 2006 16:39:05, Denis Haskin, errata?
And also in the first figure, I think BusinessObject[?] should actually be PersistentObject[?], no?
 Thu, 12 Jan 2006 07:52:24, nraynaud, memory
how in the world did the brain cells forming the neural network for that memory survive all these years and yet I can’t seem to remember my current cell phone number?

In fact while aging (and even most neural diseases, like Parkinson's), the memory fixing mecanism is altered, not the memory store. You keep read-only access.
 Thu, 12 Jan 2006 09:18:01, Philip Nelson, Repository?
What you call an adapter is often called a repository. In in that style, the test would(may) have to interact with the repository or other repositories for dependents. And those might have to be supplied to the InsuranceClaim[?]. And some of those might need repositories. And....

So, what you are saying is right good design, but may still be pretty hard for the legacy code to conform to, and it will still be hard to convince the team that this is easier. And then there is that whole leap to dependency injection to deal with. I must be feeling grumpy this morning.
 Thu, 12 Jan 2006 22:04:36, Tim Ottinger, Had you read about Python Adaptation?
I read about this and thought it was a marvelous set of ideas, but haven't had any opportunity to put it into use. More the pity. I read it over from time to time to help keep it from stale-ing in my head: Python Adaptation PEP 246