Skip to content

Instantly share code, notes, and snippets.

@jaume-ferrarons
Last active November 19, 2020 16:49
Show Gist options
  • Save jaume-ferrarons/d2633bb6b304be22b5543ca4ccb4274e to your computer and use it in GitHub Desktop.
Save jaume-ferrarons/d2633bb6b304be22b5543ca4ccb4274e to your computer and use it in GitHub Desktop.
Demonstrate the usage of SOLID principles with basic examples
# Using SOLID principles in Python
##
# Single-responsibility principle (from wikipedia): states that every module, class or function in a computer program should have responsibility over a single part of that program's functionality, which it should encapsulate. All of that module, class or function's services should be narrowly aligned with that responsibility.
##
from typing import Any, List
from abc import ABC, abstractmethod
def data_generator(rows: int = 10) -> List[int]:
return [i for i in range(rows)]
def compute_stats(data: List[int]):
return {
'# examples': len(data),
'sum': sum(data),
'avg': sum(data)/len(data)
}
print(compute_stats(data_generator()))
# It applies Single-responsibility principle because each function performs a specific functionality
##
# Open–closed principle (from wikipedia): "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification";[1] that is, such an entity can allow its behaviour to be extended without modifying its source code.
##
class Human:
def salute(self) -> str:
return "Hello"
class Me(Human):
def salute(self) -> str:
res = super().salute()
return res + ", it's me!"
print(Me().salute())
# Able to extend the functionality without modifying the original class
##
# Liskov substitution principle (from wikipedia): object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.)
##
class Introduction:
def start(self, human: Human) -> str:
return f"{human.salute()}\nHow are you?"
intro = Introduction()
print(intro.start(Human()))
print(intro.start(Me()))
# Able to use the same Introduction class with Human and Me classes
##
# Interface segregation principle (from wikipedia): no client should be forced to depend on methods it does not use.[1] ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them. Such shrunken interfaces are also called role interfaces.
##
# Bad
class Animal(ABC):
@abstractmethod
def swim(self) -> None:
pass
@abstractmethod
def bark(self) -> None:
pass
# Good
class Animal:
pass
class Fish(Animal):
def swim(self) -> None:
print("Just keep swimming")
class Dog(Animal):
def bark(self) -> None:
print("Wof")
##
# Dependency inversion principle (from wikipedia):
# A. High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).
# B. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
##
class Storage(ABC):
@abstractmethod
def write(self, key, value) -> None:
pass
@abstractmethod
def read(self, key) -> Any:
pass
class ListStorage(Storage):
def __init__(self) -> None:
self.data = list()
def write(self, key, value) -> None:
self.data.append((key, value))
def read(self, key) -> Any:
for k, v in self.data:
if k == key:
return v
class DictStorage(Storage):
def __init__(self) -> None:
self.data = dict()
def write(self, key, value) -> None:
self.data[key] = value
def read(self, key) -> Any:
if key in self.data:
return self.data[key]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment