Last active
November 19, 2020 16:49
-
-
Save jaume-ferrarons/d2633bb6b304be22b5543ca4ccb4274e to your computer and use it in GitHub Desktop.
Demonstrate the usage of SOLID principles with basic examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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