The Single Responsibility Principle

There are different ways of putting this principles:

  • “An object should have a single responsibility.”

  • “A class should have only one reason to change”

  • “A module should be responsible to one, and only one, actor.”

The principle is closely related to the principles:

  • Separation of Concerns

  • and the first rule from the UNIX philosophy: Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features”.

Example

Bad Practice

“…You had one job” — Loki to Skurge in Thor: Ragnarok

A class should have only one job. If a class has more than one responsibility, it becomes coupled. A change to one responsibility results to modification of the other responsibility.


class Animal:
    def __init__(self, name: str):
        self.name = name
    
    def get_name(self) -> str:
        pass

    def save(self, animal: Animal):
        pass

The Animal class violates the SRP. How does it violate SRP? SRP states that classes should have one responsibility, here, we can draw out two responsibilities: animal database management and animal properties management. The constructor and get_name() manage the Animal properties while the save manages the Animal storage on a database. How will this design cause issues in the future? If the application changes in a way that it affects database management functions. The classes that make use of Animal properties will have to be touched and recompiled to compensate for the new changes. You see this system smells of rigidity, it’s like a domino effect, touch one card it affects all other cards in line.

Good Practice

To make this conform to SRP, we create another class that will handle the sole responsibility of storing an animal to a database:


class Animal:
    def __init__(self, name: str):
            self.name = name
    
    def get_name(self):
        pass


class AnimalDB:
    def get_animal(self) -> Animal:
        pass

    def save(self, animal: Animal):
        pass

When designing our classes, we should aim to put related features together, so whenever they tend to change they change for the same reason. And we should try to separate features if they will change for different reasons. - Steve Fenton