How to SOLID!

Magnum Fonseca
7 min readJul 28, 2020

S.O.L.I.D is an acronym that represents five pillars of object-oriented programming and code design theorized by Uncle Bob (Robert C. Martin)
around the 2000s. And all developers should have a clear concept of it for developing software properly to avoid bad design.

Caring about your code and how other engineers, either yourself in the future, will read it and see it's quality is undoubtedly the mission of any engineer who really cares about your product and your colleagues. Use best practices points to reduce code complexity, the coupling between classes, separating responsibilities and well defining the relations between them. These are simple ways to improve code internal quality.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. — Martin Fowler

A good design reveals Intent, provides insights into requirements analysis, is adaptable, and tolerates the evolution of business needs and also accepts new technologies without undue cost.

But an understandable code doesn’t just happen! You have to craft it, You have to clean it and You have to make a professional commitment.

Professional commitment?

Make it your responsibility to craft code that: Delivers business value, make sure the code is clean, is tested and simple.

I’ll try to explain SOLID Principles in the simplest way so that it’s easy for beginners to understand.

S — Single Responsibility Principle (SRP)

A class should have one and only one reason to change

One class should serve only one purpose, this does not presume that each class should have only one method, but they should all relate directly to the responsibility of the class, otherwise, one actor is the source of that change to that class.

Following this Employee Class:

How many reasons does it have to change?

1- calculateSalary() belongs to the CFO team;

2- save() belongs to the CTO team;

3- printReport() belongs to the COO team;

Let's assume Sthefanie wants to change the rule to the business rule of calculateSalary and Pedro wants to change the format of printReport this will cause a merge conflict. When responsibilities are mixed in the same source file, collisions become more frequent.

Then Pedro adds a new Report, the report looks pretty good. Business users are happy. However, during the next payroll cycle, all paychecks have the wrong amount. When responsibilities are co-located, accidental breakage becomes more likely.

How to identify classes that violate SRP?

They are difficult to understand, It changes for too many reasons, There are subject to frequent CM collisions and merges. The classes cause a transitive impact and are subject to deployment thrashing.

O — Open Close Principle (OCP)

Entities should be open for extension, but closed for modification.

We are able to change or extend the behavior of a system without changing any existing code.

When we must change a module, we must also re-test, re-release, and redeploy that module and when we change old, working code, we often break it.

Software entities (classes, modules, functions, etc.) should be extendable without actually changing the contents of the class you’re extending. If we could follow this principle strongly enough, it is possible to then modify the behavior of our code without ever touching a piece of the original code. A change should be low cost and low risk.

Given the class:

This code is a mess and is obscuring its content, a good module should read like a story. And the story can be obscured by little decisions, odd flags, or Strange function calls.

Symptom: Switching on Type

Rigidity: Switch statements are repeated across many clients of Employee.

Fragility: Each will be slightly different and those differences must be located and understood.

Immobility: Every module that has a type case depends on all the cases.

Solution:

This code tells its own story, It doesn’t need comments and there are few obscuring details. The Client object has hidden most details behind an abstract interface, this is what Object Oriented Design is all about.

Not Rigid: A new ChargeClassifications can be added to the Client class without modifying any existing code.

Not Fragile: Don’t have to search through code looking for switch statements.

Mobile: Charge can be deployed with as many or as few Charge-Classifications as desired.

OCP is not an excuse for doing more than is necessary, Abstraction is expensive you should use it when there is an imminent risk of pain, in general, prove to yourself that it will be worth the cost.

By creating appropriate abstractions, we can produce modules that are open for extension but closed for modification.

L — The Liskov Substitution Principle (LSP)

This principle states that a derived class must be substitutable for its base class.

Inheritance is a relationship between abase class and its derived classes, each derived class and all clients of its base class.

Here is the original formulation: "If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."

In other words, Instances of a derived class must be usable through the interface of its base class without clients of the base class being able to tell the difference, Subtypes must be substitutable for their base types.

LSP can also be described as a counter-example of Duck Test: “If it looks like a duck, quacks like a duck, but needs batteries — you probably have the wrong abstraction”.

Symptoms of LSP Violations

  • Abstract methods do not apply to all implementations.
  • Lower level details become visible in high-level policy.
  • Base classes need to know about their subclasses.
  • Clients of base classes need to know about the subclasses.
  • We violate OCP when we let details creep up the hierarchy.

Following our last example, no apparent OCP or LSP violations.

Substitutability in software is about how something behaves, not about what.

  • LSP is a prime enabler of OCP.
  • Substitutability supports extensibility without modification.
  • Polymorphism demands substitutability.
  • Derived classes must uphold the contract between their base class and its clients!

LSP is all about how it behaves-as-a and not how it is-a. Inheritance != IS-A

I — The Interface Segregation Principle (ISP)

Clients should depend only on methods they call.

A client should never be forced to implement an interface that it doesn’t use or clients shouldn’t be forced to depend on methods they do not use.

Fat interfaces

Methods are grouped for ease of implementation and there are more methods than any one client needs. The problem is Phantom Dependency!

A change on behalf of one client impacts all clients and a new client impacts all existing clients.

In general an interface should be small. But sometimes a class with a fat interface is difficult to split, the good news is that you can fake it.

Notes on ISP

ClientGateway still has a fat interface, but clients don’t know this because all dependencies point AWAY from it, phantom dependency has been eliminated, and changing existing interfaces or adding new interfaces do not impact other interfaces, and therefore do not impact other clients.

D — The Dependency Inversion Principle

Low level details should depend on high level policy.
High level policy should be independent.

Entities must depend on abstractions not on concretions. It states that the high level module must not depend on the low level module, but they should depend on abstractions.

Sales depend upon all derivatives of Client, but only because it creates them. It only calls methods in Client.

In procedural designs source code dependencies flow with control, this makes it easy to add functions to existing data structures without changing the data structures.

In OO designs dependencies can be inverted so that source code dependencies flow against control.

Solution: Abstract Factory [GOF]

Dependencies are inverted, Sales no longer depends on derivatives of Client.

Conclusion

SOLID might seem to be a handful at first, but with constant usage and adherence to its guidelines, it becomes a part of you and your code, but they are principles, not rules, So case by case it must be studied to understand if the principle itself applies or not.

Are the SOLID principles applicable to Functional Programming? Of course!

Functional programmers want to separate their code to avoid crosstalk between responsibilities and users. They want to minimize the number of modules affected by a change.

Pass on what you have learned. — Yoda, Master.

--

--