SOLID is a popular term in the world of laymen's software engineering. It's just a rewrite of basic OOP concepts - there's nothing new in it. But it was made from terms rummaged from the past 30 years, and those are fun. It's not an acronym - the letters don't stand for a phrase. It's 5 different things, in 5 different styles.

As a reference, and to explain what I mean about them being just a rewrite, let''s review the basics of modern OOP. This is the stuff you know after a year-long freshman programming class:

Classes should be designed by first making an interface describing how others can use it. They should be large enough to be useful, but not so large they're cumbersome. They should be abstract - write setDisabled() instead of changeColorToDisabled(). Roughly that gives us encapsulation and loose coupling. If you want multiple versions of a class to use as plug-ins, have them all inherit from a base class.

A real explanation has details and examples - chapters of them, but that paragraph is the outline. Small encapsulated classes taking advantage of base classes to communicate. On to the 5 things in SOLID:

The "D" is "Dependency Inversion". This is an old phrase aimed at 1970's programmers. It essentially says to take the way you programmed back then, and flip it on its head - totally invert it. So clearly, not much help to non-1970's coders, but looking back is really interesting:

Old-style programs were centered on the exact format of the data. CPU's were slow and space was tight - memory sizes were a few hundred kilobytes with reel-to-reel tape as a back-up. We needed to preplan to cram our data. We used tricks like compressed formats, "just knowing" lengths, 1/2-bytes, rounding, and double-using slots as numbers or letters. Worse, everyone using data needed to know and work with these hyper-optimized formats. We weren't stupid. We knew what a huge pain that was. We knew that having a nice interface would make coding much, much simpler. But it wasn't practical.

That changed gradually. Memory sizes started doubling, CPU's became faster to where memory access was often the bottleneck, and the programs we wanted written got larger in scope. The days of ruthless efficiency were fading. By the late 70's researchers were working on sluggish but much easier to use languages, and by the late 1980's OOP-style programs were completely practical. If you weren't paying attention, the programming style "inverted" from data-first into data-hidden. Dependency Inversion is merely an archaic term for encapsulation.

S stands for "single responsibility principle". It's about how to break out program into parts, which is often a tough problem. SRP says to make one class for each "responsibility". That's a nice word: "a class should do one thing" doesn't sound right. "...have one job?" is closer. "A class should have one responsibility" -- bingo. But in the end it's just word play. Dividing a program into "responsibilities" vs. dividing it into classes - same thing either way.

SRP is somewhat useful as an introduction. If a class does way too much, then the code inside turns into a big, messy non-OOP program (sometimes we call that a "god class"). If several classes are always used together in the same way, maybe they should be combined. SRP is an odd, overly scientific-sounding way of saying "try not to make classes to big or too small".

I is the "Interface Segregation Principle". It says not to make your interfaces too big - you should segregate them into smaller ones. But an interface is how you talk to a class, so it's just another way of saying not to make classes too big, which SRP already says. This rule is a repeat. We could eliminate it and turn solid into SOLD. But there's more fun history behind this letter:

Back in the day, an interface was one file with functions that worked together to do something. There wasn't any formal Interface type like Java has. A too-big interface wasn't anything to do with classes, which didn't exist yet. It meant you should split your interface file into more, smaller files. Common examples of too-big interfaces were functions with too many completely different types of inputs. An animal creation function might require flight speed, type of horns, and frequency of shedding skin. interface segregation felt like a good term. It's not the fault of the people who used it -- the problem is bringing it back to use for the letter I.

"L" is the only one about polymorphism, but only one small part of it. It says subclasses need to be consistent. For example, suppose a Button class with a placeButton() function has 2 sub-classes. If the button would go off the edge of the screen, one class lets it, while the other forces it in bounds. Neither way is wrong, but we need them to be consistent since Button b; could be aimed at either type.

The official name is Liskov Substitution Principle which is more cool history:

In the 80's software engineering conferences were inventing object oriented programming - trying to figure out how it would work in a real computer language. That involves lots of math and proofs and making up new terms. A paper from Barbara Liskov described the consistency problem, said the subtypes need to be able to substitute for each other, then had lots of math explaining what that meant. Like most things, OOP was invented by dozens of PhD's, over years of meetings.

There's only one example of LSP on the internet. Start with a resizable Rectangle class -- width and height functions -- then have Square inherit from it. It's width and height functions need to be overwritten to keep it square: r1.setWidth(4) also changes height to 4, and vice-versa. Since r1 could be a Rectangle or a Square, that's confusing. That's not a great example since it's an unfixable class design problem. LSP is really about where the class design is fine and you made a mistake with some functions not lining up.

As a side note, square/rectangle is an example of a different thing. In real-life, a square is a type of rectangle, but that doesn't mean it should be in a computer.

The O rule is about not messing with tested general-purpose code. Here's what happens: as you're coding and come to a problem, it's oh so tempting to make just a tiny little change in some shared class. All the change does is handle your one special case, and can't possibly mess up anything else. In reality, it probably will. You may not have completely remembered how the class worked, or you somehow made a small mistake in the part you added. There's a word for it: regression testing checks for things that were working, and no one even touched them, but somehow they broke. O stands for Open/Closed, where the Closed part means "when it's done, it's done". The Open part is much more vague. To prevent people from wanting to make little tweaks, a class should be adaptable. That's all Open means.

The words Open and Closed are from another 30-year-old paper in the days when we were working out how OOP should work. It meant a base class (except we didn't have base classes back then) that you could inherit from (another thing we were in the process of inventing). As usual, the terms were fine back then, but don't make much sense today.

To sum up, SOLID is a way to explain basic OOP, except in the wrong order, using awkward outdated terms, leaving a lot out, and light on examples. On the plus side, it spells a macho-sounding word.



Comments. or email