Tuesday 27 July 2021

SOLID

Nerdy Monk with Kittens

SOLID
, YAGNI and DRY are acronyms commonly used to describe good programming practices.

DRY is not WET

DRY
is my favourite: Don't Repeat Yourself... instead of writing the same code twice, wrap it up in a function and put it somewhere where it can be easily found and re-used.  This also means if you have to fix a problem, or improve your code, you'll only have to make your change once.

I tend to take DRY to the extreme, by writing little tools, either in PowerShell, or Python, or a little desktop app that takes care of any monotonous task that I might have. The tasks are usually things to do with my dev environment, like automatically powering up useful AWS resources when I login to Windows, and adjusting settings in JetBrains' Rider, so that it only has to compile the bare minimum it needs in order for me to debug the code I'm working on. Other tools I've written include swapping in and out fake and real libraries that my code connects to, so that I can debug under a variety of conditions. These tools not only save time, but allow me to focus on what I enjoy, and what really matters.

DROP is WET

I'd love to invent my own acronym here, DROP: Don't Repeat Other People.

Whenever writing your own generic code, there should be a few things that come to mind... Why has no-one else written this before? Oh, maybe they have. Is there a NuGet package I can reuse? Because, really, if it's generic, and your code is better than any other open source library out there, perhaps you should be either creating, or contributing to open source.

YAGNI... unless it's a kitten!



YAGNI means You Ain't Gonna Need It.  The theory is that your code will be simpler if you delete (or don't write) code that you may need in the future.  It also means you don't have to test it.








SOLID is five principles:

Chaos from multiple responsibilities

1. Single Responsibility: 

A class should only have one responsibility.

Example:  A Person class should not contain code that ensures the email address is in a correct, standard email address format.  Email validation is generic and should go elsewhere.

2. Open / Closed Principle:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

To quote Robert C. Martin (Uncle Bob): 

When a single change to a program results in a cascade of changes to dependent modules, that program exhibits the undesirable attributes that we have come to associate with “bad” design. The program becomes fragile, rigid, unpredictable and unreusable. The open-closed principle attacks this in a very straightforward way. It says that you should design modules that never change. When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works.[1]

This means, if you have created a class that is being used, don't make changes unless you're fixing bugs.  Inherit from it instead, and make the changes there. Of course all the cool kids are preferring composition over inheritance these days, so adding a property that's an object is fine, but work with abstractions. When I do this, I might use an interface like IContainX, and then implement a new interface IContainY. That way, your object can be extended with any existing code being referred to (X) remaining unchanged.

If you used TDD properly to build the class, and you're not actually changing the behaviour... You can make performance improvements as long as your original tests pass.

3.  Liskov Substitution Principle:

Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

So, if the Tesla class inherits from the Car class, you might not want to have a method Car.FillUpWithPetrol(), or you should simply not create a Tesla class inheriting from the Car class.

4.  Interface Segregation Principle:

Many client-specific interfaces are better than one general-purpose interface.

The reasons:
  • Because any implementation should ideally implement every member.  When testing, and creating fakes, mocks or stubs, we don't want to have to care about whether or not a member is implemented if it's not related to the test.
  • Unnecessary complexity (See my blog post on reducing complexity).  Not everyone who flies in a plane wants to see all the controls in the cockpit of a Boeing 747.  All they may need is a button to call the flight attendant.

5.  Dependency inversion principle:

Depend upon abstractions, rather than concretions.

This is often done using dependency injection. It's a way of loosely coupling objects. You don't solder wires from a lamp into a wall socket... you create interfaces, which both the lamp and source of electricity can use.

The way SOLID's used is a bit funny.  I say it's funny, because when I interview someone they usually remember SINGLE RESPONSIBILITY and waffle through the rest. Yup, we're only human. I've heard the word SOLID used a few times, when the speaker's intention was to refer to single responsibility. I don't think it really helps to ask about it at interviews, directly. It's possible that a good answer just shows that the person's attended many interviews. Perhaps a better test would be to offer some code, and ask what the interviewee would change about it. Although, when I've done that in the past, I tend to get a lot of silence... so who knows?

References:

No comments:

Post a Comment

How to reduce complexity and know everything.

Quality We have a great QA. His code is like a Rolex. You look at his gherkin, and smile.  It says what it's testing, in plain English, ...