SOLID Design Principles

SOLID Design Principles

In object-oriented computer programming, SOLID is acronym and stands for a set of five design principles documented by Robert C. Martin. Keep your code in line with SOLID make software designs more clear, and you’re much more likely to end up with a modular flexible and maintainable. SOLID also mesh well with testability.

Single Responsibility Principle

It states that there should never be more than one
reason for a class to change. Every module, class, or function should have responsibility over a single part of the functionality provided by the software, and will have only one reason for modification. Writing test for code that follows the SRP is essentially expressing our understanding of the problem and specifying the expected behavior. We can test the class independently. When we add some more functionality, the old tests shouldn’t fail.

Open–Closed Principle

The open/closed principle states software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. This can be achieved by abstraction, polymorphism and several design patterns e.g. strategy, composite, decorator, factory method. Classes that delegate specific responsibilities to other objects allow tests to substitute a test double when needed to simulate a specific scenario.

Strategy Pattern

Liskov Substitution Principle

It states that if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the program. LSP emphasize the hierarchies should exist for the right reason, embodying a valid abstraction. Class hierarchies that follow the LSP enable the use of contract tests, tests written for parent class/interface can be executed against all derived/implementation.

Liskov Substitution

Interface Segregation Principle

It states that no client should be forced to depend on methods it does not use, we should split interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them. Small interfaces keep test simple for each interface, it also improve testability by making it easier to write and use test doubles.

Dependency inversion principle

The dependency inversion principle says code should depend on abstractions not on concretions, it suggests that a class shouldn’t instantiate its own collaborators but rather have their interfaces passed in. The dependency inversion principle is a specific form of decoupling software modules. When following this principle, the conventional dependency relationships from high-level module to low-level module are reversed, thus rendering high-level modules independent of the low-level module implementation details. The principle states:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions(interfaces).
  2. Abstractions should not depend on details. Details (classes) should depend on abstractions.

The both high-level and low-level objects must depend on the same abstraction. The interaction between a high level module and a low-level one, the interaction should be thought of as an abstract interaction between them.

The high-level class defines its own adapter interface which is the abstraction that the other high-level classes depend on. The adaptee implementation also depends on the adapter interface abstraction while it can be implemented by using code from within its own low-level module. The high-level has no dependency on the low-level module since it only uses the low-level indirectly through the adapter interface by invoking polymorphic methods to the interface which are implemented by the adaptee and its low-level module, for example plugin.

For writing tests. the ability to pass collaborators in make possible the injection of test double fairly easy.