Solid Principles
Tags: solids, python, tutorial
Published: 2023-02-20 06:48:18

# 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.
```{python}
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

```{python}
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.

```{python}

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.

```{python}
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
```{python}
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. 

### Resources:
- [towardsdatascience](https://towardsdatascience.com/solid-coding-in-python-1281392a6a94)
- [youtube](https://www.youtube.com/playlist?list=PL6n9fhu94yhXjG1w2blMXUzyDrZ_eyOme)