Wednesday, 9 December 2009

Immutable Wrapper

I am a huge fan of immutables, especially for value objects. Even in clumsy static languages like C# and Java that seem to throw every obstacle in your way to deter you from using them I find the benefits outweigh the costs.

Sometimes though a mutable makes sense. Whether that's because of the restrictiveness of a framework or the paradigm of the language. But what if we don't want to loose the power of immutables behind the scenes?

Introducing The Immutable Wrapper Pattern!

The Immutable Wrapper is a wonderfully simple pattern because there are only two things you need to do.
  1. Create a class with only one mutable field
  2. Wrap the immutables functions.
Here's an example. You've got a copy-on-write list implementation but your binding it to some GUI control that gives you a big headache if you try and point it to a different data source.

Here's your immutable:
  class CopyOnWriteList
{
private readonly IEnumerable items;

CopyOnWriteList Add(item)
{
return new CopyOnWriteList(new List(items, item));
}
}
Now wrap it:
  class WrappedCopyOnWriteList
{
CopyOnWriteList list;

void Add(item)
{
list = list.Add(item)
}
}
There, it couldn't be simpler. So simple in fact I don't know what else to say. Enjoy!

The Singleton Killer

We all hate singletons so here's another useful refactoring pattern: The Singleton Killer.

Here's a common snippet of code we all love to hate:
class Transfer
def funds(from_number, to_number, amount)
from = AccountRepository.instance.get(from_number)
to = AccountRepository.instance.get(to_number)
from.balance -= amount
to.balance += amount
end
end
So how do we kill this beast? Simply follow these idiot proof steps:

Step One: Introduce Field
Stop asking the singleton for it's reference and store your own by creating a field:
class Transfer
@repository = AccountRepository.instance
def funds(from_number, to_number, amount)
from = repository.get(from_number)
to = repository.get(to_number)
...
end
end
Wow: it's starting to look like normal code!

Step Two: Initialize in constructor
Move the initialization to the constructor:
def initialize()
@repository = AccountRepository.instance
end
This is good but wouldn't it be better if the dependancy could be injected?

Step Three: Chain constructor
In languages where dependency injection is useful you will need to chain the constructors:
public Transfer() : this(AccountRepository.instance)
public Transfer(AccountRepository repository)
{
this.repository = repository
}
Now the AccountRepository can be injected which of course means we can mock it out!

Step Four: Extract interface (for statically typed languages)
Except statically languages: they'll need to extract an interface:
public interface IAccountRepository
{
Account Get(string number);
}

public Transfer(IAccountRepository repository) ...
Good: now you can start mocking and testing.

Step Five: Introduce container
If you haven't already got a container then get one (else ensure you a wiring up your dependencies in one place). Then get that to wire up the Transfer object for you:
container.Add(AccountRepository)
container.Add(Transfer)
transfer = container.Get(Transfer)
transfer.funds("YOUACCNUM", "MYACCNUM", 50000)
Step Six: Remove singleton
Once a container is doing all your injection for you that singleton is a waste of space: get rid of him by removing that horrible instance accessor.

Choose your strategies with this refactoring wisely: if your objective is to get a class tested then first repeat steps one-four within the same class until it is clean of all singletons. This is a good exercise in it's own right - even if you're not going to move on to test or remove the singleton completely - as you start to move all the dependancies to the constructor you gain a better picture of the classes collaborators (and the quality of design). On the other hand if your objective is to remove the singleton repeat steps one-three on every usage you can find then finish up with steps four-six. Another technique is to keep pushing the useage of the singleton up through the code so each client then takes on responsibility for passing the singleton down when it creates an instance of the class. Like so:
  Transfer.new(AccountRepository.Instance)
Then repeat steps one-three on the class you've just pushed into, then push the singleton up again up and so on until you can't push it anymore. This can be an interesting exercise in itself as it starts to push out the paths through the code base as you move through the layers and may also give you a clear point where your container needs to be. Be warned though: it can get messy (though strangely satisfying)!

On larger, messier codebases none of these approaches may be realistic in which case opt for the guerilla approach and pick them off as you find them. Though I would encourage trying to at least clean out a whole class using steps one-three even if you won't end up testing it right away. With all your dependancies in one place you'll still have a better picture of the design.

Saturday, 5 December 2009

Beware Metanarratives

Now my darlings gather round while I tell you a story. But not just any story. A story about stories. About BIG stories.

Not so many decades ago, when the French still measured their GDP on the combined weight of their philosophical works, a particular man, with the amusing name of Lyotard, came up with the concept of Metanarratives (or Grand Narratives).

A Metanarrative, simply put, is a big story, often idealistic in tone, essential a grand, universal theory which is then applied to every aspect of life. There are many good examples of metanarratives from religious narratives, humanist, to economical and social theories communist, capitalist, freemarket etc.

The IT industry is brimming with metanarratives. Whether it is Stallman's completely open source future, no software patents, where everyone collaborates and all is wonderful, or the visions of an open web which overflows with information causing the end of totalitarianism, poverty etc. or the whole proprietary vs Open Source battle of the metanarratives.

Even as we move closer to home in the ever rational world of Enterprise Software Development we too find it overspilling. Waterfall has a wonderfully convincing metanarrative. And if SOA isn't a metanarrative (of Joycean composition) with it's land of milk and honey then I don't know what is. Probably the fastest growing metanarratives in IT is that of the Agile and Lean movement.

We humans love a good metanarrative to pin our life story on. They make us feel safe and cosy; it's nice to comprehend the world through the view of a single cohesive story with a beginning, middle and happy end (which is why marketing departments love forming them). We look around us finding more and more things to embellish and support our narratives. As we gain experience we see it as evidence to the greater truth of our chosen metanarrative.

But metanarratives are not a good thing - especially in an ever shifting landscape like IT. They make us lazy and prevent us from considering other stories. And if they are considered it is as a threat (e.g. MS' campaign against Open Source). Which is a problem in it's own right: if someone has subscribed to a metanarrative then it is almost impossible to win them round to any other story. Put one metanarrative against another and your blocked - queue visions of an evolutionist and a creationist locked in a room for eternity - monkeys would have written the works of Shakespeare before either changes their mind.

Metanarratives ultimately disappoint - especially in the ever shifting landscape of IT. Not so much because they are wrong but because they cannot be right. Everything, especially in IT, cannot fit into one nice story. The world is too culturally rich and diverse. As with the thousands of varying religions, The British Empire, Communism, the American Dream, DNA architecture, eventually the world shifts into a state where the narrative becomes ridiculous to apply. Yet people will keep doing so - the continued prevalence of Waterfall in some businesses being a case in point.

My concern is that we are guilty of promoting Agile as a metanarrative. Evangelists have moved it, from being a local story about a collection of problems in software development, to a model for the entire way businesses are run. Now, let's be clear, there are many elements of Agile I strongly subscribe to and promote, and I often agree with the basis of many of these claims. However the danger occurs when the Agile movement (or any other movement) seeks to displace any other narrative and claim itself universal. That ALL software should be developed this way, and ALL businesses should model themselves to support this. That's just crazy speak!

Interestingly (in my opinion) in their original forms Agile and Lean are post-modern in their attitudes towards metanarratives. Both attempt to promote fresh thinking and new ideas and move away from grand prescriptivism and dogma, especially of those found in the industry at the time. Most importantly both movements promote the fact that there isn't one universal way. For many people it was this breaking away from the metanarrative of the time and creating space to form our own stories which made Agile so attractive. Perhaps it is the inherent paradox in the post-modernists view of metanarratives that have caused them to, ironically, become metanarratives themselves.

The world of IT is too rich and diverse and full of too many stories (some old, some new) to support metanarratives. There is nothing wrong with some of these stories (such as Lean and Agile) when viewed in isolation; some are full of rich culture and ideas, some are simply misconceived and naive. On their own they are harmless and together with other stories they are invaluable. But as soon as people start turning them into something with which to narrate the whole Industry they are deadly.

So my warning is this: beware of metanarratives. If you notice that you are tending to frame the IT world around one story, whether that is Scrum, SOA, BDUF etc. please consider whether you have been caught in a metanarrative. Equally if you find yourself embattled due to someone's particular metanarrative, don't try and slam another one around their head. Instead try and appreciate their story at a local level whilst keeping your own stories localized and nonthreatening - they may even begin to fit it into theirs.

About Me

My photo
West Malling, Kent, United Kingdom
I am a ThoughtWorker and general Memeologist living in the UK. I have worked in IT since 2000 on many projects from public facing websites in media and e-commerce to rich-client banking applications and corporate intranets. I am passionate and committed to making IT a better world.