My relationship with SOLID - The big O

Published on 2013-3-26

Open closed is dead, long live Open closed

I'm blogging about SOLID for some reason, and now we're onto the beast that set me off:

OCP

Yikes

They are “Open For Extension”. This means that the behavior of the module can be extended. That we can make the module behave in new and different ways as the requirements of the application change, or to meet the needs of new applications.

and

They are “Closed for Modification”. The source code of such a module is inviolate. No one is allowed to make source code changes to it.

Thanks Uncle Bob, you're right, this is over-stated, and because it's so over stated, I believe it to be the cause of so many of the over-designed pieces of crap I've had to deal with in my career :-)

This is the conversation I imagine developers having with themselves when writing this stuff, I don't have to imagine too hard because I've been there too:

What if somebody at some point wants to change this so they can have another behaviour for this value, I'd better use the strategy pattern here instead of this switch statement, but oh my word now I've done that what if somebody wants to use this code from some other system than this one, I'd better stick a command system in front of this and use double dispatch for handling them - but wait, what if other code needs to react from this and do something else, I'd better raise a pile of events, but what if those aren't enough I'd better make sure I split all this behaviours out into their own interfaces so they can be overridden and...

And on it goes until suddenly what was a couple of classes behind a controller or presenter blow up into a mess of a hundred classes that all do the square root of diddly squat, but together manage to cause a lot of headaches for anybody coming across the code in the future.

Now, I'm sure this wasn't the intent behind these statements, and it sure isn't now - but you know what?

Here is my current thinking on the Big O. Let's make it stand for "Open", and remove the CP.

Good code is code that another developer can change the behaviour of easily and clearly see the consequences of that change.

The decisions made when designing a language can have implications on how we satiate this need.

We can look to @jonskeet's perfect language where "all classes are sealed by default, all methods are sealed by default, all extensibility points are explicitly defined", or we can look at any of the no-holds barred dynamic languages that let us get away with pretty much anything.

Let's take option one

Let's say we do make that call (because Jon is where all of this started, so taking his perspective will help see where he is coming from), then surely if we're going to follow OCP then we have to from the very beginning bake in these extension points on a just in case basis.

Woah! No!!! Stop right there. This is how we end up with the kind of code where we use the strategy pattern everywhere and have a million and one interfaces to describe the act of doing absolutely nothing of consequence at all.

The best code is code that can be changed easily, code that is easy to read, code that keeps state close to the behaviour, code that that doesn't attempt magic tricks, code that anybody can read - this is the code meets this standard.

Let's take option two

Now we're in the magical happy land of dynamic languages, and we can just screw over any object by fiddling with its prototype, the rules have gone out of the window. This is the land of possibility people, and we have the power to change things.

Does this mean we haven't got to worry about OCP? Nay - this is not so. Having the ability to change anything is fantastic, people don't know everything when putting together a module, and having everything open by default means that while you wait for the project you're using to have an appropriate extension point you can hack around it and get on building your product.

However, explicitly defined extension points have clearly defined behaviours and are predictable - so are clearly desireable.

Wait a second

Getting off the subject of how languages can affect our decisions, we can look at how our programming culture can have an impact on it.

I find it intensely irritating that the languages that lean towards the "closed by default" design also seem to live in the environment where the code itself is also "closed by default", which means that either the framework authors have to build in extension points for everything imaginable or the users of that framework code have to suffer for it.

I find it intensely amusing that the languages that lean towards the "open by default" design also live in the environment where the code itself is open by default (this is the age of Github), which means that the people with the problem can come in, make the desired change and move on with their projects with barely any thought to it at all.

And this is where I go back on what I said in all the statements above, this is where OCP is now, times have changed since the original sentiment was uttered, we have a lot more open source now and the ideal is:

This changes the very face of things, this changes how we build software - and it means that a strict adherence to OCP becomes largely a thing restricted to stodgy enterprise environments that are slow moving, uncompetitive and slow to get things done (and they're welcome to it)

The true future of OCP is in building these open source little packages that are easily changed or forked, and in that we can find an elegance and simplicity that I find quite pleasing.

If we're forking a module and sticking a new version on it, we're saying that it is no longer the same as the old module, it is new code and the old code still exists too.

Where OCP makes sense

Summary

So, in the age of tiny disposable modules that do just one thing, OCP is dead (wink) - who'd have thunk it. /dramatic oversimplification

2020 © Rob Ashton. ALL Rights Reserved.