Solid Principles
The principle of SOLID coding is an acronym originated by Robert C. Martin, and it stands for five different conventions of coding.
Solids provide five different conventions of coding. Using these principles you can improve the reliability of your code by working on its structure and its logical consistency.
The Solid principles are:
1. The Single-Responsibility Principle(SRP)
Every component of your code(in class) should have one and only one responsibility. As a consequence of that, there should be only a reason to change it.
Example: Let us take a list that should used to print the Ascending order , sum of list.
def operation(lis):
a= sorted(lis)
#print Ascending order
print("Ascending order:",a)
#print sum of list
print("Sum of list:", sum(lis))
operation([5,3,4,2,1])
#Ascending order: [1, 2, 3, 4, 5]
#Sum of list: 15
Instead of multiple operations in one function code let us make it more readable by making each method for a operation
def get_ascending(lis):
a= sorted(lis)
"""print Ascending order"""
print("Ascending order:",a)
def get_sum(lis):
""" print sum of list"""
print("Sum of list:", sum(lis))
def main(lis):
#return Ascending
get_ascending(lis)
#return sum of list
get_sum(lis)
main([5,3,4,2,1])
#Ascending order: [1, 2, 3, 4, 5]
#Sum of list: 15
The result of this simple action is that now:
- Easier to localize errors. Any error in execution will point out to a smaller section of code, it give us faster debug phase.
- Part of the code is reusable in other section of your code.
- Easier to create testing for each function.
2. The Open–closed principle (OCP)
You should not need to modify the code you have already written to accommodate new functionality, but simply add what you now need.
from abc import ABC, abstractmethod
class Operations(ABC):
'''Operations'''
@abstractmethod
def operation():
pass
class Ascending(Operations):
def operation(lis):
print(f"The Ascending {sorted(lis)}")
class Sum(Operations):
'''Compute sum'''
def operation(lis):
print(f"The max is {sum(lis)}")
class Main:
'''Main'''
@abstractmethod
def getoperations(lis):
# __subclasses__ will found all classes inheriting from Operations
for operation in Operations.__subclasses__():
operation.operation(lis)
if __name__ == "__main__":
Main.getoperations([1,4,2,3,5])
If now we want to add a new operation e.g.: Average, we will only need to add a class “Average” inheriting from the class “Operations”. The newly formed sub-class will be immediately picked up by subclasses() and no modification in any other part of the code needs to happen.
3. The Liskov substitution principle (LSP)
If a subclass redefines a function also present in the parent class, a client-user should not be noticing any difference in behaviour, and it is a substitute for the base class.
The result of this principle is that we’d write our code in a consistent manner and, the end-user will need to learn how our code works, only one.
4. The Interface Segregation Principle (ISP)
Many client-specific interfaces are better than one general-purpose interface.
The IS principles tell us that a class should only have the interface needed (SRP) and avoid methods that won’t work or that have no reason to be part of that class.
-It occurs when A subclass inherits methods from a base class that it does not need.
from abc import ABC, abstractmethod
class Mammals(ABC):
@abstractmethod
def swim() -> bool:
print("Can Swim")
@abstractmethod
def walk() -> bool:
print("Can Walk")
class Human(Mammals):
def swim():
return print("Humans can swim")
def walk():
return print("Humans can walk")
class Whale(Mammals):
def swim():
return print("Whales can swim")
Human.swim()
Human.walk()
Whale.swim()
Whale.walk()
# Humans can swim
# Humans can walk
# Whales can swim
# Can Walk
For this example, we have got the abstract class “Mammals” that has two abstract methods: “walk” and “swim”. These two elements will belong to the sub-class “Human”, whereas only “swim” will belong to the subclass “Whale”.
The sub-class whale can still invoke the method “walk” but it shouldn’t, and we must avoid it.
Code using ISP
from abc import ABC, abstractmethod
class Walker(ABC):
@abstractmethod
def walk() -> bool:
return print("Can Walk")
class Swimmer(ABC):
@abstractmethod
def swim() -> bool:
return print("Can Swim")
class Human(Walker, Swimmer):
def walk():
return print("Humans can walk")
def swim():
return print("Humans can swim")
class Whale(Swimmer):
def swim():
return print("Whales can swim")
if __name__ == "__main__":
Human.walk()
Human.swim()
Whale.swim()
Whale.walk()
# Humans can walk
# Humans can swim
# Whales can swim
Now, every sub-class inherits only what it needs, avoiding invoking an out-of-context (wrong) sub-method. That might create an error hard to catch.
5. The Dependency Inversion Principle (DIP)
Abstractions should not depend on details. Details should depend on abstraction. High-level modules should not depend on low-level modules. Both should depend on abstractions.
Example:
Imagine that you have a program that takes in input a specific set of info (a file, a format, etc) and you wrote a script to process it.
What would happen if that info were subject to changes?
You would have to rewrite your script and adjust the new format. Losing the retro compatibility with the older files.
However, you could solve this by creating a third abstraction that takes the info as input and passes it to the others.
Conclusion:
Keeping these principles in mind while designing, writing, and refactoring your code so that your code will be much more clean, extendable, and testable.
Top comments (0)