SOLID is an acronym for the five object-oriented design (OOD) principles by Robert C. Martin in his book Design Principles and Design Patterns.
Adopting SOLID programming principles can also contribute to avoiding code smells, refactoring, and Agile or Adaptive software development.
In this post we'll be learning about the second SOLID programming principle.
Objects or Entities (classes, modules, functions) should be open for extension, not for modification.
Let's take an example to understand what this means:
class Animal: def __init__(self, name): self.name = name def get_name(self): pass def animal_sounds(animals): for animal in animals: if animal.name == "lion": print("roar") elif animal.name == "mouse": print("squeak") animals = [ Animal("lion"), Animal("mouse") ] animal_sounds(animals)
The above code violates the Open Closed principle as when new animals are added to the
animals array we need to modify the animal_sound() function. For every new animal, new logic needs to be added and the same change would be done at multiple places if our application becomes more complex, as is the case with actual projects.
What is an Interface?
An interface is a description of the actions that an object can do. For example when you flip a light switch, the light goes on, you don't care how, just that it does.
In Object Oriented Programming, an Interface is a description of all functions that an object must have in order to be "X".
Again, as an example, anything that "ACTS LIKE" a light, should have a turn_on() method and a turn_off() method. The purpose of interfaces is to allow the computer to enforce these properties and to know that an object of TYPE X must have functions called A, B and C.
On a high level it acts like blueprint for designing classes.
How to use Interfaces to conform to Open Closed Principles?
Python doesn't enforce all the classes implementing the interface to define all the interface's abstract functions unlike other languages (Java, C#, C++ etc.)
So let's take a look at how to make this work in Python.
If you are interested in learning how other languages implement interfaces you can take a look at this Digital Ocean's blog.
class Animal: def __init__(self, name): self.name = name def get_name(self): pass def make_sound(self): pass class Lion(Animal): def make_sound(self): print("roar") class Mouse(Animal): def make_sound(self): print("squeak") class Cow(Animal): def make_sound(self): print("moo") def animal_sounds(animals): for animal in animals: animal.make_sound() animals = [ Lion(name="Big Cat"), Mouse(name="Squeakie") ] animal_sounds(animals)
Animal class now has a
make_sound abstract function. Other animal classes (Lion, Mouse etc.) have extended the Animal class and implemented their own definition of make_sound function.
The animal_sounds function iterates over the animals and calls their implementation of make_sound function.
Now if a new animal needs to be added we don't need to change the Animal class, we can implement a new class for that animal and animal_sounds() will call the implementation of make_sound function for that animal.
Thus the Animal class now conforms to the Open Closed Principle of Programming.
This post is Part 2 of the series, Back to Basics, each covering one SOLID principle.