Interview Questions, Answers and Tutorials

SOLID principles

SOLID principles

SOLID is a set of five principles that help us write better code. Think of it as a guide to building strong Lego structures that don’t fall apart easily. Each letter in SOLID stands for one principle. Let’s break them down one by one with some Python code examples.

1. Single Responsibility Principle (SRP)

What it means: Every class should have only one job or responsibility. Imagine if you had a robot that could do your homework and also cook dinner. It would get confusing! It’s better to have one robot for homework and another for cooking.

Example:

# Bad example
class Robot:
    def do_homework(self):
        print("Doing homework...")
    
    def cook_dinner(self):
        print("Cooking dinner...")

# Good example
class HomeworkRobot:
    def do_homework(self):
        print("Doing homework...")

class CookingRobot:
    def cook_dinner(self):
        print("Cooking dinner...")

2. Open/Closed Principle (OCP)

What it means: Classes should be open for extension but closed for modification. Imagine building a Lego tower. You should be able to add more blocks on top without changing the blocks at the bottom.

Example:

# Bad example
class Bird:
    def make_sound(self):
        return "Tweet"

class AnimalSound:
    def get_sound(self, animal):
        if isinstance(animal, Bird):
            return animal.make_sound()
        # If we add more animals, we need to modify this method

# Good example
class Animal:
    def make_sound(self):
        raise NotImplementedError

class Bird(Animal):
    def make_sound(self):
        return "Tweet"

class Dog(Animal):
    def make_sound(self):
        return "Bark"

class AnimalSound:
    def get_sound(self, animal):
        return animal.make_sound()

3. Liskov Substitution Principle (LSP)

What it means: If a class (say Dog) is a subclass of another class (say Animal), then we should be able to replace the parent class (Animal) with the subclass (Dog) without breaking our code. It’s like saying any type of car (sports car, truck) should still work like a basic car.

Example:

# Bad example
class Bird:
    def fly(self):
        print("Flying...")

class Ostrich(Bird):
    def fly(self):
        raise Exception("Ostriches can't fly!")

def make_bird_fly(bird):
    bird.fly()

# Good example
class Bird:
    def move(self):
        print("Moving...")

class FlyingBird(Bird):
    def move(self):
        print("Flying...")

class Ostrich(Bird):
    def move(self):
        print("Running...")

def make_bird_move(bird):
    bird.move()

4. Interface Segregation Principle (ISP)

What it means: A class should not be forced to implement interfaces it doesn’t use. Imagine if you had to learn to swim even if you only wanted to learn to ride a bike. That’s too much!

Example:

# Bad example
class Robot:
    def cook(self):
        pass
    
    def clean(self):
        pass
    
    def wash_dishes(self):
        pass

# Good example
class CookingRobot:
    def cook(self):
        print("Cooking...")

class CleaningRobot:
    def clean(self):
        print("Cleaning...")

class DishWashingRobot:
    def wash_dishes(self):
        print("Washing dishes...")

5. Dependency Inversion Principle (DIP)

What it means: High-level modules should not depend on low-level modules. Both should depend on abstractions. Imagine building a Lego car where the wheels should fit any kind of car, not just one specific model.

Example:

# Bad example
class Engine:
    def start(self):
        print("Engine started")

class Car:
    def __init__(self):
        self.engine = Engine()
    
    def start(self):
        self.engine.start()

# Good example
class EngineInterface:
    def start(self):
        raise NotImplementedError

class ElectricEngine(EngineInterface):
    def start(self):
        print("Electric engine started")

class GasolineEngine(EngineInterface):
    def start(self):
        print("Gasoline engine started")

class Car:
    def __init__(self, engine: EngineInterface):
        self.engine = engine
    
    def start(self):
        self.engine.start()

electric_engine = ElectricEngine()
car = Car(electric_engine)
car.start()

Practice Questions

  1. Single Responsibility Principle: Refactor the following class to adhere to the Single Responsibility Principle.

class Person:
    def __init__(self, name):
        self.name = name

    def save_to_database(self):
        print(f"Saving {self.name} to the database")




  1. Open/Closed Principle: Refactor the following code to follow the Open/Closed Principle.

class Shape:
    def draw(self):
        pass

class Circle(Shape):
    def draw(self):
        print("Drawing a circle")

class DrawingTool:
    def draw_shape(self, shape):
        if isinstance(shape, Circle):
            shape.draw()




  1. Liskov Substitution Principle: Identify the violation in the following code and fix it.

class Bird:
    def fly(self):
        print("Flying")

class Penguin(Bird):
    def fly(self):
        print("Penguins can't fly")




  1. Interface Segregation Principle: Refactor the following code to follow the Interface Segregation Principle.

class Worker:
    def work(self):
        print("Working")

    def eat(self):
        print("Eating")

class Robot(Worker):
    def work(self):
        print("Working")

    def eat(self):
        pass  # Robots don't eat




  1. Dependency Inversion Principle: Refactor the following code to follow the Dependency Inversion Principle.

class LightBulb:
    def turn_on(self):
        print("LightBulb turned on")

class Switch:
    def __init__(self):
        self.lightbulb = LightBulb()

    def operate(self):
        self.lightbulb.turn_on()




Solutions

  1. Single Responsibility Principle:

class Person:
    def __init__(self, name):
        self.name = name

class DatabaseSaver:
    def save_to_database(self, person):
        print(f"Saving {person.name} to the database")




  1. Open/Closed Principle:

class Shape:
    def draw(self):
        pass

class Circle(Shape):
    def draw(self):
        print("Drawing a circle")

class Square(Shape):
    def draw(self):
        print("Drawing a square")

class DrawingTool:
    def draw_shape(self, shape: Shape):
        shape.draw()




  1. Liskov Substitution Principle:

class Bird:
    def move(self):
        print("Flying")

class Penguin(Bird):
    def move(self):
        print("Swimming")




  1. Interface Segregation Principle:

class Worker:
    def work(self):
        print("Working")

class Eater:
    def eat(self):
        print("Eating")

class Human(Worker, Eater):
    pass

class Robot(Worker):
    pass




  1. Dependency Inversion Principle:

class Switchable:
    def turn_on(self):
        raise NotImplementedError

class LightBulb(Switchable):
    def turn_on(self):
        print("LightBulb turned on")

class Switch:
    def __init__(self, device: Switchable):
        self.device = device

    def operate(self):
        self.device.turn_on()

lightbulb = LightBulb()
switch = Switch(lightbulb)
switch.operate()




Now you know the SOLID principles! These help make your code more like a sturdy Lego tower—easy to build on and hard to break. Happy coding!