Sponsored Link •
|
Summary
I have been looking mock objects and test driven design lately. The main issue, I see brought up over and over again is that people want to modify existing code by inserting mock objects, but that it is hard to do because of code dependencies. I argue that proper use of abstraction at the implementation level would make it much easier.
Advertisement
|
I was perusing Michael Feather's blog and his entry entitled Frustration Driven Development caught my eye. Particularly because it talked about a desire for new programming languages which always grabs my attention!
The post is very interesting in that it does a good job summarizing points that I have seen made elsewhere with regards to difficulty in inserting mock objects into existing code. From what I have seen so far, it appears that the problem lies not in lack of features in the languages used, but rather ineffectual use of abstraction in code. To try and paraphrase the example is essentially that given the following code :class Account { private balance = 0; private log = new TransactionLog(); deposit(value) { log.addEntry(balance, value); balance += value; } ... }The goals is to create a test case without having to actually use the TransactionLog class implementation as it stands because it is too slow (establishes a connection to a database and commits changes and all of that). So we want to insert a mock object for TransactionLog.
class Account { private balance = 0; private log = new MockTransactionLog(); deposit(value) { log.addEntry(balance, value); balance += value; } ... }Michael isn't advocating this particular Bank Account design, he was trying to illustrate that he seeks a way to modify existing code by inserting mock objects at particular points. I don't have a way to modify existing code inside of a class without rewriting the class, but I do have a way of writing classes in such a way that we can reduce dependencies (i.e. decrease couping). For me this is a perfect example of why abstraction is useful at the implementation level and it ties in with my previous blog entries (OOP Case Study: A Bank Account class Part 1 and Part 2) about how an Account class should be built from the group up using different layers of abstraction. Here we have an Account class which is overloaded with responsibility, it is the lowest level of representation of the Account, but it also carries the responsibility of commiting all deposits to a log. The purpose of abstraction is to separate responsibilities into different layers of abstraction. The account class would have been much better off if it was written as two classes and one interface:
class RawAccount { private balance = 0; deposit(value) { balance += value; } ... } class TransactionalAccount { RawAccount acc; private log = new TransactionLog(); deposit(value) { log.addEntry(balance, value); acc.deposit(value); } ... } interface IAccount { deposit(value); ... }Whereever the original Account class was used it should be replaced with the IAccount interface type. This alone though still hasn't solved the problem though of replacing TransactionLog() with a mock object but it does do one thing, it makes it easier to refactor existing code. In order to have a mock object it would be easy and logical to add a template parameter to TransactionAccount which provides the concrete type of the transaction log:
class TransactionalAccount<class LogType> { RawAccount acc; private log = new LogType(); deposit(value) { log.addEntry(balance, value); acc.deposit(value); } ... }This small change requires a rewriting of only the relatively few points where the TransactionalAccount is instantiated. This is an example of how increased abstraction reduces code coupling making code easier to manage and modify. Some may argue that we can't always change existing code, and I agree. It is though a very reasonable practice to instill in an organization which is attempting to follow a test driven design methodology. We can't always change existing poorly designed code, but we can always encourage better coding practices in the future. If programming history has taught us anything it is that more abstraction is generally a good thing.
Have an opinion? Readers have already posted 19 comments about this weblog entry. Why not add yours?
If you'd like to be notified whenever Christopher Diggins adds a new entry to his weblog, subscribe to his RSS feed.
Christopher Diggins is a software developer and freelance writer. Christopher loves programming, but is eternally frustrated by the shortcomings of modern programming languages. As would any reasonable person in his shoes, he decided to quit his day job to write his own ( www.heron-language.com ). Christopher is the co-author of the C++ Cookbook from O'Reilly. Christopher can be reached through his home page at www.cdiggins.com. |
Sponsored Links
|