SOLID - is it still relevant?

SOLID is an acronym for a bunch of object-oriented principles. Actually, no. Not any more. SOLID is now just an ACRONYM! Forget everything you knew or thought you knew of these principles, because they're all wrong, AND THEY'RE NOT EVEN PRINCIPLES!

... bit harsh... but there's been discussions in the community about these principles that people have been using for years as a guide for good software design; discussions about what these 'principles' really mean and the redundancy and overlap between them.

I've been a fan of SOLID for a long time. Before SOLID (since the 90's), I've studied and used patterns, practices, (and there were even some laws, like Law of Demeter). SOLID fitted nicely in with the other principals that came before it (spoiler: they're based on the principals that came before it) and they were something I used and recommended. When doing code reviews, I would reference the respective principle, and when interviewing candidates, I would ask them if they knew what the principles were.

Kevlin Henney did a fantastic presentation that describes both the principles, and why they're not principles and what's wrong with each of them.

This post goes through each of the principles and summarises what Kevlin said in his presentation and then my thoughts.

Single Responsibility Principle #

A class should have one and only one reason to change, meaning that a class should have only one job

What even is a 'Responsibility'? Why just one reason to change? This is really about 'cohesion' where things that change together, stay together.

CRC cards (Class, Responsibilities, Collaborators) were mentioned in the presentation, which brought back memories for me. These are index cards where you write down (with a pen, pencil, quill) the 'Class name, Responsibilities, and Collaborators'. Because they were index cards, they were small. This space limitation turned out to be a great feature as you couldn't fit that much on the things! Therefore, you had few responsibilities and collaborators, and your class name was (mostly) well thought out.

It seems that the main point of contention that Kevlin has for this one is the Single in Single Responsibility. It should be few rather than single, and is more about cohesion.

The principle is based on a guideline from the book 'Object Solutions' by Grady Booch:

"Every class should embody only about 3-5 distinct responsibilities.".

The Single (and Principal) were added later.

Responsibilities relate to each other and are what makes a class coherent. If you break these down into individual responsibilities, they no longer relate to each other, and the type loses cohesion. The presentation mentions a paragraph from a blog by Glenn Vanderburg (emphasis mine):

... they seem to belong together, and it would feel somewhat unnatural to pull them apart

Glenn says that the term 'coupling' is something everyone understands, but 'cohesion' is less used and less familiar. To teach the concept of cohesion, Glenn said he used the etymology of the word: cohesion comes from the same root word as adhesion comes from; you can think of things that are 'adhesive' as needing to be stuck together as they are not naturally the same - but with cohesion, the naturally stick to each other (cohere) because they are well matched and have things in common.

In my opinion, this 'principle' has served us well: OK, it's not about 'single', but it has got developers thinking and talking about whether their types are too big, or have different responsibilities, or are doing too much.

Open Closed Principal #

Objects or entities should be open for extension but closed for modification

Kevlin's argument here is that OCP is really a 'language design principle'. The quotes that Bob Martin originally used for this principle were from Object Oriented Software Construction by Bertrand Meyer. The book was not just teaching OO design, but also teaching OO language design. It is argued that OCP is purely Inheritance. Nothing more than that.

As Dan North describes in his blog post, this was good advice years ago before version control systems, versioned package management systems, and automated refactoring tools. Back then, code was expensive and risky to change and was mostly 'additive'. I remember inheritance was wildly popular because it allowed this very thing: you could extend stuff without modifying the original. But the price you paid was tight coupling (mostly through inheritance) and increased complexity.

The advice now though is: "Change the code to make it do something else!". If it's a breaking change, increment the major version number!

It is common to think of OCP as a plug-in architecture. Plug-in architectures are useful and likely do use (hopefully limited) inheritance, but OCP is not plug-in architectures.

I've certainly used the term OCP to describe plug-in architectures. In fact, I think that's the only use of the term that I've made. Having lived with the consequences of inheritance overdose in the 90's, I certainly wouldn't use or recommend OCP in its purest sense, which is literally just inheritance.

Liskov Substitution Principle #

Objects of a superclass shall be replaceable with objects of its subclasses without breaking the application

Barbara Liskov's original quote was more strict than the reinterpreted version in SOLID. Hers was for abstract data types. The SOLID version for OO is is about extending types without breaking behaviour.

But by extending any type, you are changing its behaviour.

By extending any type, you are naturally implying derivation via inheritance, or the is-a relationship.

Kevlin used an example of deriving a type named ThreeDPoint from TwoDPoint. If TwoDPoint has an X and Y, then according the SOLID version of LSP, passing a ThreeDPoint instead of TwoDPoint would fall well within the realms of this principle. But the types are not the same; a 3D point can be projected onto a 2D plane, but that's a function, and functional transformations are not related to types.

Bad choices of inheritance (but choices that still abide by this principle) result in method spam. To demonstrate, he went on to show another example of a RecentlyUsedList deriving from List. By deriving from List, the derived type has access to lots of methods it doesn't need. So, by doing this, we're compliant with the [modern interpretation of] Liskov Substitution Principal, but we've broken the Interface Segregation Principle.

I said this above re. Open Closed Principle:

Having lived with the consequences of inheritance overdose in the 90's, I certainly wouldn't use or recommend OCP in its purest sense, which is literally just inheritance

... and I have a similar opinion re. LSP: Extend via small interfaces rather than inheritance and reap the benefits of being more loosely coupled. Which leads to...

Interface Segregation Principle #

Keep interfaces small so that users don’t end up depending on things they don’t need.

Kevlin said in his presentation that ISP is basically

"Single Responsibility Principle applied to interfaces"

.. which I thought summed it up very nicely.

Of all the items in SOLID, ISP must be the one I use the most, and certainly the one I quote the most when pairing or doing code reviews. For years now I've used the term role based interface, which is where an interface has a role, and, generally, has just one method. I learnt some history of the term 'role-based' from a slide in Kevlin's presentation:

So, what is an example of a non-segregated interface? Here's one:

public interface IClientRepository {
Client GetClient(int id);
void DeleteClient(int id);
}

Most users of this interface will want just one of those methods. With role-based interfaces, we'd have:

public interface IGetClients {
Client GetClient(int id);
}

and

public interface IDeleteClients {
void DeleteClient(int id);
}

So, from the CORBA description of roles, the administrative interface is IDeleteClients, and the interface used by normal users is IGetClients

There a few big benefits from keeping interfaces small:

All is well so far with this principal, except, it's not a principle...

meme of woman shouting at cat saying YOU SAID THEY WERE PRINCPLES!

Kevlin showed the definition of Principle to be:

"a fundamental truth"

and

"morally correct behaviour and attitudes"

Interface Segregation cannot be a Principle because there is a language concern; not all languages have the concept of an Interface

A better term would be Pattern which is a context specific piece of advice. Kevlin is great at providing examples, and the example he used here was:

Before crossing the road, always look left, then right, then left again.

This is great advice in the UK, but not so much in Germany or other countries where they drive on the wrong side of the road!

OK, so let's call it a Pattern rather a Principal. I still think it's a good Pattern.

But there's one other issue: if you've applied Single Responsibility Principle, then you have nothing left to do.

I agree with that but I would also say that having ISP (Interface Segregation Pattern) is useful as another way to identify issues relating to responsibilities and coherence.

Dependency Inversion Principle #

Depend in the direction of abstraction. High level modules should not depend upon low level details

In Kevlin's presentation, he says this this is the least disagreed with 'principle' but that the term inversion is steeped in 'old language terms' based on structured programming: "inversion of the normal order" is outdated. Inversion is the normal. He says Dependency Inversion isn't really a separate principle, but is rather a combination of Single Responsibility Principle and Liskov Substitution Principle.

Dan North has a different take on this. He says there's nothing fundamentally wrong with DIP but describes the inverting of dependencies for every dependency as an obsession.

My opinion is that wiring each implementation to an interface does seem like overhead, and most of the time there is no 'multiple choice' of implementations. As Dan says:

"Most dependencies don't need inverting, because most dependencies aren't options, they are just the way we are going to do it this time"

I think DIP is pretty fundamental; giving it a new name (that ends in Pattern) would be good. In fact, is it even a pattern (or an architecture style)?


Summary #

The SOLID Principles have gained enormous popularity/notoriety. I think their very existence has had a positive impact on the craft of writing software. Whether or not the principles are inaccurate or just plain wrong (or even redundant), the fact that people are aware of them and discuss/debate them is positive.

We do need a new SOLID though; a refinement of the current principles patterns in SOLID, and perhaps with some new entries, maybe from object calisthenics?

πŸ™πŸ™πŸ™

Since you've made it this far, sharing this article on your favorite social media network would be highly appreciated πŸ’–! For feedback, please πŸ¦‹ ping me on Bluesky! πŸ¦‹

Leave a comment

Comments are moderated, so there may be a short delays before you see it.

3 comments on this page

  • Jan

    commented

    Excellent article! I highly appreciate your effort (and ability) to think things through as opposed to just falling back on dogmatism. Thank you for sharing.

  • Dola

    commented

    πŸ—£πŸ”₯πŸ”₯πŸ”₯πŸ”₯πŸ”₯

  • Dola

    commented

    πŸ—£πŸ”₯πŸ”₯πŸ”₯πŸ”₯πŸ”₯

Published