There’s a group of guidelines and principles to help us write clean code in object-oriented programming–commonly called SOLID–which was introduced by Robert Martin (aka Uncle Bob).
The five principles of SOLID are:
- Single Responsibility Principle
- Open-Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Right now I’m going to explain the Single Responsibility Principle with a real world example.
Let’s define it
The Single Resposibility says that a class or module should only have one reason to change. In other words, the methods and variables of a class should change for the same reasons.
Why does it matter?
If a class has more than one responsbility, then it might need to be changed for more than one reason. Changing one responsbility might then inhibit your class to meet its other responsbilites which could cause your program to break. And we don’t want our code to break, right?
Real world example
Let’s say you are creating a Ruby program for a university. The Admissions Counselor wants to keep track of each student’s name, gender, age, and major so you add those instance variables to a Student class. You even create a birthday function so that you can change a student’s age as he/she becomes a year older. You also decide to create a function that lists what books the student will need based on his/ her major, and therefore, his/her courses. Each student needs to have books afterall.
This might seem okay at first, but I think you’ll quickly realize that this class now has multiple responsibilities and multiple reasons to change. What if the Admissions office decides that it wants to change the data they store for each student? They decide to stop keeping track of gender and add hometown instead. Well that’s probably okay. But then what if the dean of one program decided to change which books each student in a specific major needs? You can now see that we have two very different reasons to change variables and methods of the same student class.
Instead of using one class, the Single Responsbility Principle tells us that we should split the student class into at least two classes. We might want to split them up into a person class and a major class like below.
#Here's our first class that violates the single responsbility priciple. class Student attr_accessor 'name', 'gender', 'age', 'major' def initialize(name, gender, age, major) @name = name @gender = gender @age = age @major = major end def birthday @age += 1 end def books if @major == "Spanish" return Spanish_books #Assuming we have an array of books for each major elsif @major == "Biology" return Biology_books else return Business_books end end end
#Here's our second set of classes that agree with the single responsbility priciple. class Person attr_accessor 'name', 'age', 'hometown' def initialize(name, age, hometown) @name = name @age = age @hometown = hometown end def birthday @age += 1 end end class Books attr_accessor 'major' def initialize(major) @major = major end def books if @major == "Spanish" return Spanish_books #Assuming we have an array of books for each major elsif @major == "Biology" return Biology_books else return Business_books end end end
People are the reasons for change
The methods in each class now have the same reasons to change. Why? Because the people who care about the data being stored in each class (deans and admissions counselors) care about the same things.
This leads me to my favorite point made by Uncle Bob on the 8th Light blog: “As you think about this principle, remember that the reasons for change are people. It is people who request changes. And you don’t want to confuse those people, or yourself, by mixing together the code that many different people care about for different reasons.”
Make each class responsibile for only one thing and your code will be cleaner, easier to read, less likely to break, and easier to move forward with as your application becomes complex.