While I’m coding, I usually have a bunch of very helpful pixies hanging around my desk. (They’re Dan’s pixies really, since he thought of them first; I’m just borrowing them.)
The pixies are bored, and just waiting for a job to do. So, when I’m coding a class, they look out for opportunities to help out that class. When I’m coding the Game of Life, for instance, I write a Gui class that lets me toggle the cells on the grid. Then I have to work out what happens when I toggle the cells.
I could do it in the same class – in the gui – but fortunately the pixies step in to prevent me making these poor design decisions. “Oh, I’ll do that for you!” one of the pixies calls out. (They usually start with this phrase, and they’re all called Thistle.)
“Thanks, Thistle! Do you know what you’re doing?”
“Um, not really. What’s toggling a cell? Why’s that valuable? What is it you want me to do for you again?”
“I need you to handle the cell living and dying when I toggle it.”
“Oh, okay!” Thistle says. “I don’t like the ‘and’ word so much, though. It makes me feel like I’m doing two things at once. What do you call that? The living and dying thing?”
“Hm.” I think about it while the pixie taps his foot impatiently. “I’d call it a lifespan, maybe. Can you handle the cell lifespan for me? Just let me toggle the cells. I also need you to tell any observers that there have been some changes to the cells, and give them a way of finding out where those changes are. I think they’ve already got an idea of what they want there.”
“Really? Both things?”
“Well, there’s no point doing one if you don’t do the other. It’s all part of the same role.”
“If that’s what’s valuable to you then I’ll do it,” he says. “Just pretend I’m there for the moment; I’ll be back when you need me.”
“Fine,” I say. So I use a mock pixie in place of the real one. I create an interface which does what the pixie’s going to do:
(See, it’s an interface that starts with “I”, and it represents a role that the pixie is playing for me. This is a role-based, anthropomorphized interface.)
So, now we have code which compiles. Of course, the real code in the Gui is null, or maybe a null object pattern – I might create something like
IHandleCellLifespans.KILLING_THEM_ALL if I’m feeling particularly mean. But that’s all right, because Thistle the pixie will step in when it’s time.
So, I run the code. I’ve usually written an automated scenario. It doesn’t matter whether I run the scenario or step through the game manually; both result in the same thing happening, or not happening – no matter where I click the mouse, no cells appear. Pixies are notoriously unreliable.
Since I can’t rely on the pixies, I inject a new class to handle the dependency instead. I decide to call it the
Engine, for the moment, and I write an example of how to use it and what it should do for me.
The next step is the
Next button. I think about how this would work in the
Engine, and start writing some code to show how the
Engine needs to behave. I’ll need to calculate the number of neighbours, and apply the rules accordingly.
One of the pixies pipes up, “Oh, I’ll do the neighbours!” and another one says, “Oh, I’ll handle the rules!”
“Fantastic!” I’m so trusting; I always forget what these pixies are really like when it comes to getting the work done.
“If I’m going to count the neighbours,” Thistle says, “I’ll need some information about where I’m counting from, and what’s around me.”
“Ah, I can get the information from the cell itself,” I say.
“No, don’t do that. It’s fine where it is; I’ll just sit inside the cell and do it from there. Can you give me something that lets me know where the other cells around me are? Then I can do the work for you.”
“Sure,” I say, “the
Engine knows where the life is. I’ll just give you access to the
Engine and let it play that role for you.”
“Can’t I do it instead? I’m bored,” one of the other pixies asks. “Just give me the information from the engine, and let him talk to me instead.”
Of course, when I try to run it then I find out that all the pixies have mysteriously vanished, and I end up assigning the role to the Engine anyway, or one of its anonymous inner classes. Having it defined as a different role means that it’s easy to move this responsibility around. Maybe I’ll create a World to look after the cells, and let that do the job instead; the pixies certainly aren’t very helpful.
“What do you mean, we’re not very helpful?” Thistle says. “Look at your code. You haven’t written anything that isn’t needed by something else, so there’s less code to maintain. Because we jump in all the time to try and do jobs for you, every time you can assign a new responsibility to something else, you do – that’s the single responsibility principle in action; none of your classes are doing too much. And you can replace us with something else that does the job at any time – that’s the Liskov Substitution principle. The roles we perform are clearly named. It’s been easy to describe the behaviour of each class using mocks to stand in when we’re not there, and the examples are very readable. You can also use them to work out whether your code still works or not, by running them as tests.”
“Okay. I can’t see myself relying on you guys for bigger, enterprise stuff, though.”
“What do you mean?” Thistle looks offended. Oops.
“Well, let’s say that I’ve got a shop, and I need the tills to talk to stock control.”
“Controlling stock? Oh, I’ll do that!” one of the pixies announces excitedly.
“What, all on your own?”
“Well, I’ll probably delegate it to a team, but that’s my responsibility – you don’t need to worry about it. I’ll be there when you need me. Just pretend I’m there for now. How would you like to find me? What kind of stuff are you going to send to me, and what do I need to do with it? What would you like back?”
So I start with something simple – a URL that I’ll use to find Thistle the pixie, some domain objects that I want to send him, and some objects that I’d like back in return. We talk about how to get the information across, whether some of the tills might provide different stock information, how to talk to the claims department about the quality of the goods we’re selling, and whether I’ll be okay if the claims he gives me have more information than I need.
“Hold on,” I say. “You’ve got me playing this game now. I’m not a Claims Department. I’m not going to do the job myself. I’ve got a home to go to!”
“Meh, never mind,” Thistle replies. “I’ll be sitting with this team over here, coding the stock control. We’ll just pretend you’re doing the job; we’ll mock you out for now.”
“How will I know that I’m doing the job correctly?”
“We’ll have to talk to each other occasionally. Is that going to be hard? We’ll write some scenarios over in our team that describe how we’re going to use you.”
“What if I make a mistake?”
“Do you know what mistakes you’re going to make already?”
“No,” I confess. “I’m sure there will be some, though.”
“When you make a mistake, we’ll deal with it at that point. Sound good?”
I think about it. I reckon I could write some code that pretends to be doing the job of the Claims Department and responds correctly to the way they’ve described how they’re going to use me – just for those examples – then I could go home and Thistle would never know. I knock up a quick stub and slip it into the stock control team’s scenarios, then I disappear too, just like the pixies. I figure I’ll start coding a real Claims Department that does a more robust job tomorrow.
When I get back the next morning, all the pixies performing the role of Stock Control have been replaced with code too. The Stock Control team claim that they’ve never even seen them.
I corner Thistle again. “You’re really not very helpful, are you?”
Thistle looks sulky. “Of course we are! Look at your architecture. You’ve got simple messages going back and forth. Your code is very tolerant of extra information, as is the code on this side. You’ve got lovely RESTful URLs, because you were thinking about how you’d like to find us, instead of us providing you with some weird mechanism that doesn’t match exactly what you wanted. We’ve got clean interfaces and APIs. There are no extra columns in your database, because you only replaced exactly what we said we’d do in the first place. You’ve got scenarios to describe how we work together, and at a unit level examples to describe how you’re delegating responsibility to the other pixies. It’s a lovely, maintainable system with a fairly flat cost of change. Isn’t that what you wanted?”
I nod thoughtfully. “I think it would have been easier for me to just write the code instead of going through you all the time.”
“Ah,” says Thistle, “but then you’d have code that was easy to write, instead of code that’s easy to use.”
I think about how they made me fill in the role of the Claims Department. “You never did any of the work, though! I could have done that job myself; put myself in each of those roles and then replaced myself with real code. That would have let me create consumer-driven interfaces just as easily as using you.”
Thistle shrugs. “If that’s what works for you, sure.”
“Won’t people think I’m a bit mad? If I start talking about how I’m personally going to use a particular class, or how I’m offering to do a job for another?”
Thistle looks at me with raised eyebrows, then gestures at all the other pixies clustered around my desk.
“I think it’s a little late to be worrying about that now,” he says.