SOLID design principles
- Robert C. Martin (Uncle Bob)
1 Single responsibility principle (SRP)
- SOC (separation of concern)
- class:
- has primary responsibility
- should not take other responsibilities
class Journal:
def __init__(self):
self.entries = []
self.count = 0
def add_entry(self, text):
self.entries.append(f"{self.count}: {text}")
def remove_entry(self, pos):
pass
# break SRP
def save(self, filename):
file = open(filename, "w")
file.write(str(self))
file.close()
def load(self, filename):
pass
def load_from_web(self, uri):
pass
- Why save breaks SRP?
- persistance handled here. Need to change every class if method changes
- Solution: take responsibility of persistance in a separate class
class PersistenceManager:
def save_to_file(journal, filename):
file = open(filename, "w")
file.write(str(journal))
file.close()
Takeaway
- Do not overload.
- Antipattern: God object, everything in a single class
2 Open-closed principle
Classes should be open for extension and closed for modification
Example:
- We have a class
ProductFilter - Implement method to
filter_by_color. - Goes to production.
- Want to add filter by size.
- You add another method
filter_by_size.
Violates Open-closed principle
Why? Classes should be extended not modified.
May need more methods.
filter_by_size_and_color,filter_by_size_or_color
Solution:
- Use enterprise pattern -
Specification
Takeaway
Don't want to mess up class already in production
3 Liskov Substitution principle (LSP)
You should be able to substitue a base type for a subtype
- a function works on base class but not on derived class
- inheriting
Rectangleclass to makeSquare
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it
4 Interface Segregation principle (ISP)
- do not stick too many elements into an interface
Example
- Create a
Machineclass with methodsprint,fax,scanRaiseNotImplementederror. - A class like
MultiFunctionprinter needs all methods. - But what about a
OldFashionedPrinter. It cannot implement fax. - Either
passOR - Raise
NotImplementederror
Idea of ISP
- keep things granular instead of keeping everything
- Instead of
Machineclass createPrinterclass - create
printmethod - Another class called
Scanner - Implement
spanmethod - Combine two classes or use just one.
Takeaway
Creating Interface with too many methods is not a good idea. Split into smallest interface, so that people do not need to implement more than they need to.
5 Dependency Inversion Principle (DIP)
-
Not related to dependency injection.
-
High level classes should not depend on low level modules. Instead they should depend on
abstractioni.e. onabstractmethods or classes.
Example
- define
Person - define
Relationsship - add an attribute
relationsas a list. - define
Research - Uses
relationslist to print relations between twoPersoninRelation - Problem: If
relationsis changed to a different data structure,Researchdoesn't work.
Solution
- Do not depend on concreate implementation of
relationsas list. - create methods like
find_all_children
Summary
- Single Responsibiility Principle - A class should only have one reason to change
- Separtion of concerns
- Open-closed principle
- Classes: open for extension but closed for modification
- Liskov Substitution Principle
- Able to substitute base type for a subtype
- Interface Segregation Principle
- Do not put too much into single interface
- YAGNI - You Ain't Going to need it
- Dependency Inversion Principle
- High-level modules should not depend on low-level ones; use abstractions