Archive for

February 2007

Curse You Apple

So it looks like I’ve managed to butcher the power adapter/power socket on my PowerBook. I’ve managed to keep it in absolute pristine condition for the last 2 years or so, but a few months ago I accidentally kicked the power adapter causing the cable to go flying out. No big deal, the green light came back on and all has been well.

Until now. For some reason, I now can’t get it to charge at all. I plug the cable in, nothing, no light comes on and no charge is being picked up.

Onto the Apple Store I go to figure out how much it’s going to cost to replace. £55. And that’s ignoring the fact that I can’t order one because it’s showing delivery of 2-4 weeks.

My only option is to hope the Apple Store on Regent Street have some, and that I can somehow get over there in the next evening or so. Oh, and that they carry them. And that it’s my power adapter and not my PowerBook. If kicking the cable causing it to come out of the socket kills the socket, that has to be a particularly duff piece of design.

Ah well, fingers crossed it all works out well. My Aperture and Rails dabblings will have to wait another day - I’ve just about used the last of my remaining charge to write this.

Posted

Extract Client Interface Refactoring

Think about how you refactor your code. You write the test, pass the test, then take a step back and see how you could improve things. It’s often not until you take that strategic view that you can decide whether you made the best decisions in the heat of battle. But, the beauty of the technique is you get that second look. Safely.

I was pairing a few days ago on my current project and my pair and I managed to work ourselves into a bit of a twist over a single test. The end result was a couple of headaches and over an hour and a half of time lost. But, it was the source of a lot of talking. One of the things Jeff (my pair) mentioned was something I’d never really thought of before, and a light clicked.

The way we refactor code is from the wrong side.

A key part of the power of Test Driven Development (and other such techniques) is that they force you to think about what you’re trying to do first, before you dive in and just start doing it.

Say you’re writing a data provider for a chart that should look at all the transactions for a client and plot percentages. Already you’ve made a statement about your code and what it’s interactions with some collaborating objects are. If you’re using mock objects to help write and design your code, chances are you’ll be writing expectations of how those dependent objects are to be used. The interface of your collaborating objects has been exposed through writing your test.

One suggestion in the 2004 paper, Mock Roles Not Objects (pdf), is that you should only mock types you own. It’s something I tend to forget, and will be trying harder not to in future. If you don’t, your test ends up being coupled to code you don’t own and an unrelated test could break should the external interface you depend on change. Instead, you write a thin wrapper that is defined closer to your need. This is one such use of mocks- proving you can integrate well with other code - but, more importantly, mocks free you into focusing on testing one thing at a time and specifying how you want to talk to other things.

All this is demonstration of a client interface-oriented approach.

As a developer sitting down to work, I’m interested in knowing how I can use other types for my own to get it’s job done. If I’m writing a chart data provider that plots the last 5 months worth of transactions, I’m not interested that I get a client or a market, I just want something that implements the interface I’ve defined through writing my test. I shouldn’t be using concrete classes from the domain, but rather interfaces for the things I’m interested in. The client interface. I don’t care about anything else.

The key thing of note is that it’s the client that determines the interface. It’s not about what I as a class do, but what others do with me.

However, of the refactoring tools and IDEs that I’ve used (shamedly small I know :), all of them are focused around the following steps:


  1. Extract an interface from the concrete class.

  2. Introduce selected members from the concrete class to the interface

  3. (Potentiall) replace dependent classes’ use of the concrete class with the interface.

Step 3 reveals the flaw. We’re saying that we’ll change the interface that was exposed to other classes, not that other classes ask us what they think of us.

In the previous example of a type that had been introduced as a result of writing some mock-based tests our need-driven interface has been changed from the wrong side. Our need-driven interface was evolved through writing how our type was going to be used. Only in such a need-driven situation are we able to understand the intent of the interface. Extracting an interface from the concrete class (the wrong side) results in us making decisions away from where the intention is. Bad.

Instead, imagine a situation where we have a concrete class that is collaborating with a number of dependent classes. Wouldn’t it be great if you could refactor from directly within that situation, when you’re working with the class under test and (most importantly) surrounded by intent. To have the refactoring tool show what you’re using on the classes and suggest you pull those into a client interface. You make the refactoring decision based on what your type needs from others, rather than what your other types can give you, and when you know what you’re actually using them for. When you understand what the object is.

If we’d had such a tool we could have spotted that we were mocking a third party library, and missing a valuable opportunity to see what we were truly dependent on, what we truly needed. We missed an opportunity to introduce a wrapper interface for what we actually needed. We weren’t really interested in a Market or Client to do our work, but rather just something that had Transactions. Our headaches would’ve been avoided!

Posted
Fork me on GitHub