The aim of this paper is to make the readers familiar with the concepts of SOLID principles. They are essentially a set of practices that leads to well designed software (Readable,Modular,Scalable)
Single Responsibility Principle
Basic gist :
- Every class must have a single responsibility to fulfill
- If a class is performing too many tasks, especially if the tasks are logically unrelated, then you would be better off writing another class.
How it plays out :
A bad example :
#Class below is reponsilble for user properties and user database management
class User:
def __init__(self, name: str):
self.name = name
def get_name(self) -> str:
pass
def save(self, user: User):
pass
- If some changes are passed to the database management system, all the classes working with user properties will have to be recompiled to compensate for new changes.
The correct way :
#Simply split the classes.
class User:
def __init__(self, name: str):
self.name = name
def get_name(self):
pass
class UserDB:
def get_user(self, id) -> User:
pass
def save(self, user: User):
pass
Open/Closed principle
- This principle states that classes should be open to extension, not to modification. If you want to add some functionality to a class, you are better off extending it, avoiding violating the Single responsibility principle.
A bad example:
class Discount:
def __init__(self, customer, price):
self.customer = customer
self.price = price
def give_discount(self):
if self.customer == 'fav':
return self.price * 0.2
if self.customer == 'vip':
return self.price * 0.4
The above class is responsible for giving out discounts, however modifying the class to add the vip discount condition violates the open/closed principle, instead do this:
The correct way:
class Discount:
def __init__(self, customer, price):
self.customer = customer
self.price = price
def get_discount(self):
return self.price * 0.2
class VIPDiscount(Discount):
def get_discount(self):
return super().get_discount() * 2
Here, we create a new class that handles the vip discount method.
Liskov Substitution principle
The objects of a sub class should be able to replace the objects of its parent class without any changes in behavior.
class Bird():
def fly():
pass
class Duck(Bird):
pass
class Ostrich(Bird):
pass
The above example is a bad implementation, Considering ostriches can't actually fly, We need our subclasses to be able to replace parent classes while still being logically consistent.
class Bird():
pass
class FlyingBirds(Bird):
def fly():
pass
class Duck(FlyingBirds):
pass
class Ostrich(Bird):
pass
The example above is a good implementation of the concept.
Interface Segregation principle
An interface can be imagined as a contract, if you decide to implement an interface, you have to do so in its entirety
-
So what if you have interfaces with too many functions, all the classes will have
to implement all the functions even if it just needs one or two of them.class IShape: def draw(self): raise NotImplementedError class Circle(IShape): def draw(self): pass class Square(IShape): def draw(self): pass class Rectangle(IShape): def draw(self): pass
You can implement multiple interfaces, so its better to have smaller and more specific interfaces.
Dependency Injection principle
Imagine a scenario where you're directly instantiating classes into other classes. Everytime a class needs to be instantiated, all the classes that its dependent on will need to be instantiated as well, incurring a massive overhead.
-
This is solvable by simply giving the responsinbility of instantiation of objects to another entity.
class AuthenticationForUser(): def __init__(self, connector:Connector): self.connection = connector.connect() def authenticate(self, credentials): pass def is_authenticated(self): pass def last_login(self): pass class AnonymousAuth(AuthenticationForUser): pass class GithubAuth(AuthenticationForUser): def last_login(self): pass class FacebookAuth(AuthenticationForUser): pass class Permissions() def __init__(self, auth: AuthenticationForUser) self.auth = auth #Object is injected here. def has_permissions(): pass class IsLoggedInPermissions (Permissions): def last_login(): return auth.last_log
The AuthenticationForUser
object is passed into the constructor instead of being instantiated inside the class
Top comments (0)