SOLID principles using Typescript

alemarr
4 min readApr 5, 2020

--

“If the bricks aren’t
well made, the architecture of the building doesn’t matter much.” — Clean Architecture (Uncle Bob)

This article aims to introduce SOLID principles and give some insight on their benefits when applying them.

We’ll go through each one of them and show some examples using Typescript.

What is SOLID?

SOLID is an acronym for five software design principles introduced by Robert C. Martin in the 2000s, that aim to help us structure our code in order to:
• Tolerate change.
• Ease code understanding.
• Write components that can be used in many software systems.

SOLID stands for:

  • S: Single Responsibility Principle
  • O: Open-Closed Principle
  • L: Liskov Substitution Principle
  • I: Interface Segregation Principle
  • D: Dependency Inversion Principle

Now, let’s go through each one of them.

Single Responsibility Principle

A class should have one, and only one, reason to change.

If our classes assume multiple responsibilities, they will be highly coupled thus making them more difficult to maintain.

What’s a reason to change?

Uncle Bob states that this principle is about people. This means that when you write a software module, and changes are requested on that module, those changes can only originate from a single person or a tightly group of people representing a single narrowly defined business function.

Another definition for this principle is:

Gather together those things that change for the same reason, and separate those things that change for different reasons.

This can also be considered the definition of Separation of Concerns.

The following piece of code shows a violation of the SRP in which theBook class is both a representation of an entity and also implements the persistence of such entity.

By applying Separation of Concerns, we split the Book class to have the representation of the book in a class and the persistence logic in another one:

Open-Closed Principle

Software entities should be open for extension, but closed for modification.

This principle states that software entities must be extensible without having to modify the existing code. In order to achieve this, we need to make abstractions. By doing this, we’ll be able to extend the behavior of a class without changing a single line of code in it.

The following snippet shows an AreaCalculator class that accumulates the areas of different shapes, that will have to be modified every time we add a new Shape:

A solution for this would be to implement a Shape interface in every shape. This way we implement a simple method to calculate the sum of the areas. Every time we need to add a new shape, it will implement the Shape interface and we won’t have to make any changes on the calculator.

Liskov Substitution Principle

Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.

This principle states that objects must be replaceable by instances of their subtypes without altering the correct functioning of the system.

A classic example of a violation of this principle is the Rectangle-Square problem. The Square class extends the Rectangle class and assumes that the width and height are equal. When calculating the area of a square, we’d get a wrong value.

I solved this by doing the same I did for Open-Closed Principle . I implemented a Shape interface that will have to be implemented by every new shape we add.

Interface Segregation Principle

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

This principle states that classes should never implement interfaces that they don’t need to use. If they do, we’ll end up having not implemented methods in our classes. This can be solved creating specific interfaces instead of general-purpose interfaces.

The solution is splitting VehicleInterface into specific interfaces:

Dependency Inversion Principle

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.

This principle states that a class should not depend on another class, but instead on an abstraction of that class. It allows loose-coupling and more reusability.

Here, the Post class depends on the MemoryStorage class to save new posts. What happens if we need to change the storage used to save posts? We’ll have to modify the PostService class to change the type of the db property, thus violating the Open-Closed Principle .

If PostService relies on a interface instead of a class, we wouldn’t have to make changes on it.

These principles represent the cornerstone of state-of-the-art software. When combined together, they make it easier for programmers to develop code that’s easier to understand, extend and maintain.

Whether you’re just learning about programming or you’re an experienced developer, I hope you enjoy this story.

Thanks for reading! :)

--

--