SOLID

2023-03-31 Class Object-Oriented Programming OOP Design

SOLID are five object-oriented design principles, compiled by Robert C. Martin in his work Design Principles and Design Patterns. First letters of the acronym represent those principles:

  • S – Single-responsibility principle
  • O – Open-closed principle
  • L – Liskov substitution principle
  • I – Interface segregation
  • D – Dependency Inversion principle

Their goal is to simplify reuse and make software easier to maintain and extend.

From the paper, here are symptoms of rotting design:

  • Rigidity - software is difficult to change, which tends to cascade to many other changes
  • Fragility - tendency to break in many places when it is changed
  • Immobility - difficulty to reuse part of a project in another
  • Viscosity - right way to do something (along a design) is difficult to do, so engineers tend to use hacks to fix problems

Most of the rotting is caused by poor design, where changing requirements cause unplanned dependencies. In order to manage them, it is necessary to keep them under control. Following principles were designed for that purpose.

Single-responsibility Principle

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

Using this principle leads to smaller classes with better defined responsibility. It is much easier to determine whether a change fits in the class.

Open-closed Principle

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

The class should be easily extensible without modifying the class itself. It might be sometimes hard to achieve, but when done, it significantly improves maintenance.

This also avoids changes in existing code (and anything that inherits it) that currently works. If code is not changed, it is unlikely to break.

Following techniques can be used to obtain this property:

  • Dynamic Polymorphism - use interfaces and put specific code into respective class
  • Static Polymorphism - templates or generics

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

Coming from Barbara Liskov‘s type theory. It simply means that anything that works on a class should be also working same way on any subclass. The interface can be considered a “contract” with users of the class, and breaking it in subclasses can easily lead to problems with polymorphism.

Good way to verify this is achieved is through the test suite. All tests that pass in parent class should also pass with the child.

Interface segregation principle

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

Interfaces should not include unrelated things and force a class to implement more than necessary. The goal is have more smaller interfaces, which are more specific.

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

Prefer interfaces over dependency on specific class. Also try to avoid creating dependency in the class itself, rather have it pushed from outside.