Christopher Anabo
Christopher Anabo
Senior Tech Lead
Christopher Anabo
SOLID

SOLID

SOLID Principles in Programming

The SOLID principles are five essential guidelines that enhance software design, making code more maintainable and scalable. They include Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion. In this article, we’ll explore each principle with real-life examples, highlighting their significance in creating robust and adaptable software systems. These five principles are:

  1. Single Responsibility Principle (SRP)
  2. Open/Closed Principle
  3. Liskov’s Substitution Principle (LSP)
  4. Interface Segregation Principle (ISP)
  5. Dependency Inversion Principle (DIP)

Single Responsibility Principle : Each class should be responsible for a single responsibility. If there more than one responsibility, it should be separate. This principle ensures that a class has only one reason to change, making it easier to maintain and test

Let’s imagine you’re building a LEGO robot that can do different things: clean, cook, and play music. Each part of the robot has a job:

  • The cleaning part is responsible for sweeping and vacuuming.
  • The cooking part is responsible for making food.
  • The music part is responsible for playing songs.

Now, let’s say the music part also starts cooking and cleaning. Suddenly, if something goes wrong with the music, it might also mess up the cooking or cleaning! Fixing the robot becomes really hard because everything is jumbled together.

The Single Responsibility Principle (SRP) says: Each part (or class, in coding) should only have one job. That way, if the cleaning part breaks, you only fix that part, and it doesn’t affect the cooking or music.

In short: One job, one responsibility, and no mixing things up! It makes things easier to fix and understand.


Open-Closed Principle : Software entities (classes, modules, functions, etc.) should be closed to modifications and open to extension. This principle ensures that the existing code is not modified, but new functionality can be added by extending the class behavior

Imagine you’ve built a robot that plays songs, and it’s working perfectly. Later, you want the robot to also tell jokes. You have two choices:

  1. Take apart the robot and add the joke-telling feature inside.
  2. Attach a new piece to the robot that can tell jokes, without changing the existing robot.

The Open-Closed Principle (OCP) says: Don’t mess with what’s already working; instead, add new features by expanding it.

So, instead of tearing apart the music-playing feature to add jokes, you build a new joke-telling feature that plugs into the robot. The robot is now open for adding new features but closed to modifying the old ones.

In short: Build it in a way where you can add cool new stuff without breaking or changing what’s already there.


Liskov Substitution Principle : Objects of a superclass should be replaceable with objects of its subclasses without breaking any functionality. This principle ensures that the behavior of the superclass is preserved in its subclasses.

Imagine you have a basic delivery robot that delivers small packages. You decide to build a more advanced robot, like a flying delivery drone, which can also deliver packages.

Now, here’s the deal: anywhere the delivery robot is used, you should be able to swap it out for the flying delivery drone without breaking anything. Why? Because both robots share the same purpose: delivering packages.

The Liskov Substitution Principle (LSP) says: If you replace something (like the basic robot) with its “child” or a more advanced version (like the drone), everything should still work the same. The new thing must do what the old thing does, and more if needed, but never less or in a confusing way.

Imagine if the drone could only fly but suddenly stopped carrying packages—it wouldn’t follow LSP because it breaks the expectation that it’s still a delivery robot!

In short: A child should behave like its parent, or better, without breaking the rules the parent followed.


Interface Segregation Principle : Do not force any client to implement an interface which is irrelevant to them. This principle ensures that a class should not be forced to implement methods that it does not need.

Let’s imagine you’re designing a super-cool LEGO robot for different tasks:

  1. A cleaning robot that sweeps floors.
  2. A cooking robot that makes pancakes.
  3. A music robot that plays songs.

Now, imagine there’s just one giant controller for all these robots. Even if the cleaning robot only needs the "sweep" button, it would still get all the buttons for cooking and music. That’s confusing and unnecessary, right?

The Interface Segregation Principle (ISP) says: Don’t force something to use buttons (or code) it doesn’t need.

Instead, give each robot its own specialized controller:

  • The cleaning robot gets only cleaning buttons.
  • The cooking robot gets cooking buttons.
  • The music robot gets music buttons.

This way, each robot works perfectly with what it needs, and there’s no extra clutter!

In short: Give smaller, focused tools (or interfaces) that match exactly what’s needed, instead of a big, one-size-fits-all tool.


Dependency Inversion Principle : High-level modules should not depend on low-level modules, both should depend on abstractions. This principle ensures that the high-level modules are not tightly coupled to the low-level modules, making it easier to change the implementation of the low-level modules without affecting the high-level modules.

let’s go back to your LEGO robots to explain the Dependency Inversion Principle (DIP) in a simple way!

Imagine you’ve built a cleaning robot. It uses a specific type of vacuum cleaner. But one day, you find a cooler, better vacuum! You try to upgrade, but your robot is built in a way that only works with the old vacuum. Annoying, right?

The Problem

Your robot depends directly on a specific vacuum cleaner. If the vacuum changes, you need to change the robot’s design too. That’s bad design because your robot isn’t flexible!

The Solution

The Dependency Inversion Principle (DIP) says:

  • Your robot should not depend on a specific vacuum cleaner.
  • Instead, both the robot and the vacuum cleaner should depend on something more general—a "Vacuum Interface."

Here’s how it works:

  1. You define a Vacuum Interface that says, "Hey, any vacuum cleaner should have a suck() method."
  2. The robot doesn’t care what kind of vacuum it’s using. It just calls suck() through the interface.
  3. Now, you can swap vacuums freely—old ones, new ones, fancy ones, cheap ones—all without changing the robot’s code!

In Short

  • Bad Design: Robot depends on specific vacuum cleaners.
  • Good Design (DIP): Robot and vacuum cleaners both depend on a shared interface, making them flexible and swappable.

In simple terms: Depend on general ideas (like interfaces), not specific things (like one brand of vacuum). This makes your

system adaptable and easier to upgrade.



What is the difference between Interface Segregation Principle (ISP) and the Single Responsibility Principle (SRP) ?

The Interface Segregation Principle (ISP) and the Single Responsibility Principle (SRP) are similar because they both promote focus and simplicity, but they solve different problems. Let me explain:

Single Responsibility Principle (SRP)

This is about what a class or module does. It says:

  • Each class should only have one job or one reason to change.
    For example: A cooking robot only cooks—it doesn't clean or play music.

Interface Segregation Principle (ISP)

This is about what a class or module is asked to do (via interfaces). It says:

  • Don’t force something to use things (methods or buttons) it doesn’t need.
    For example: Don’t give the cleaning robot a "make pancakes" button, because it doesn’t need it.

Key Difference

  • SRP focuses on splitting up responsibilities: “Do one thing only, and do it well.”
  • ISP focuses on designing interfaces: “Only give me what I need to do my job.”

Think of it this way:

If SRP is about organizing your robots to have specific tasks, ISP is about making sure their controllers don’t have unnecessary buttons. Both principles make things simpler, but from different angles!

 


Dependency Inversion and Dependency Injection are related concepts but are not the same. Here's a simple explanation of both and how they differ:


Dependency Inversion Principle (DIP)

  • What it is: A design principle (part of SOLID) that says:
    High-level modules (like your robot) should not depend on low-level modules (like a vacuum cleaner). Both should depend on abstractions (like an interface).

  • Why it matters:
    It ensures flexibility and decouples code. The robot doesn’t care which vacuum it uses as long as it conforms to the Vacuum interface. You can swap parts easily without changing the robot’s design.


Dependency Injection (DI)

  • What it is: A way to implement Dependency Inversion. It’s a technique where the dependencies (like a vacuum cleaner) are provided to an object (like the robot) by an external system rather than the object creating them itself.

  • How it works:
    Instead of the robot creating its own vacuum, you "inject" the vacuum into the robot. This can be done in three main ways:

    1. Constructor Injection: Pass the vacuum when the robot is created.
    2. Setter Injection: Pass the vacuum through a setter method.
    3. Interface Injection: Pass the vacuum through a method that the robot requires to implement.

Key Difference

Dependency Inversion Dependency Injection
A design principle that focuses on decoupling high-level and low-level modules using abstractions. A technique or pattern to provide dependencies to a class rather than letting it create its own.
What to do: Depend on abstractions, not concrete implementations. How to do it: Provide dependencies (like objects) from the outside.
More of a "rule" to follow in design. More of a "tool" or implementation strategy to achieve decoupling.