Please enable JavaScript.
Coggle requires JavaScript to display documents.
Learning S.O.L.I.D. Programming Principles - Coggle Diagram
Learning S.O.L.I.D. Programming Principles
Interface Segregation Principle (ISP)
Definition
No user should be forced to depend on the methods that he/she does not use.
In other words:
Clients should be depending on the minimal set of interface features: the fewer methods and attributes.
Identifying the dependences that are most often used
Unit test for some class or feature is small: that means that this feature is probably not necessary.
Usage
Separate the necessary client usage interfaces and the other groups
Always build a minimum function interface to handle the basic requirements
Extend the more detailed features with subclasses.
Introduction to SOLID principles
Why do we need to SOLID principles?
Benefits
Because any change to a software can cause
devastating
ripple effects that can harm the quality of the software.
Allow the separation of concerns.
The example in this course is Black Jack (21 points), and we will find why the original design is vulnerable.
What are the concerns in the bad design?
Why is it bad?
Missing responsibilities
Mixed responsibilities
Limitation of reuse
Not suitable
Haphazard Interface design
Poor constructor design
How to improve it?
What are the myth?
What SOLID is not about?
It doesn't guarantee the Agile or DevOps principles.
It doesn't concern about security issues in softwares.
It do not cover any licensing and legal issues of SE.
It doesn't cover support issues.
It doesn't tell you how to deploy the software
SOLID cannot guarantee the optimal status of a software.
Order does not matters
Overlapping between principles, and there are more:
DRY: Don't Repeat Yourself
GRASP: General Responsibility Assignment Software Principles
Liskov Substitution Principle (LSP)
(Strong Subclass Principle)
Defininition
The child class should be able to substitute the parent class of it directly without causing any negative side effects (e.g. break the program)
For example, if the User class use an Array to save log, but the subclass Admin uses a HashMap to save logs, then when we want to save Admin instance with User, that part will crash.
Tricks to keep it
In Python, you gonna to use
unit test
Because there is no way for any language to guarantee that the overrides subclass functions are having the same behaviors as the super-/parent-classes.
In Python, prevent
instanceof()
(almost never use it except for arithmetic lower basic functions)
Using default value in
Python
to assure LSP for similar but different methods (
Rethinking
)
Refactoring
and separating the common part out to parent class is also a way to handle LSP issues.
Open-Closed Principle (OCP)
Depending on the ISP and LSP to make it effective
Definiton
A class should be
open
for extension, but
closed
for modification.
E.g.
If you have a bug that need to be fixed in the super class, try to only use extension subclass to make that subclass bug-free.
If the bug cannot be fixed in the super class, and it passed the original unit test (means some corner case for clients/users showed up), then simply replace the class in the future major release.
Using warning for the deprecated bug class
Tricks
Using class extensions to make sure that OCP work.
Inheritance and composition
Extend
Wrap
More class extension methods in
Python
Functional conposition
Basically using all kinds of constructors and parameters to build new extended instances
Mixin
Decorator
Dependency Inversion Principle (DIP)
Define
The concrete class name should be appear as late as possible (preferably runtime), and no matter the level of class, they should all depend on abstract class, and not concrete class.
Core ideas
Doing unit test and
test-driven development
is the key to minimize the ripple effect of refactoring.
Mocking object injection in testing is easier if dependencies are handled well.
Single responsibility Principle (SRP)
Define
Every class should responsible to only one functionality with the appropriate level of encapsulation.
Core
Types
Controller
GRASP inspired single responsibility identification
Cohesion
When multiple functions looks closed related for each class
E.g. Blackjeck
hard()
and
soft()
functions
Indirection
When you need to link functions that provide similar but different functions
Child classes that convert data into JSON, YAML, and CSV
Cohesion
Design Process
Test Driven Design (TDD)
Core ideas
S: Are the tests properly separated
O: Can we easily find the extension points? (Such as a simple subclass from abstract class)
L: Does the super class and subclass provide the same test results
I: Does the interface testing complicated and intricate (that is probably because the interface logic is messy)
D: is the Mock object easy to deploy
CRC Card
Class Responsibility Collaborators
Techniques
Identifying all the
nouns
first
Class object
Property object
Diagrams will show the relationships
Summary
Each class should...
Have single responsibility in functionality (SRP: Single Responsibility Principle)
Open to extensions, but closed to modifications (OCP: Open-Closed Principle)
Have strong subclasses that can replace the super class without side effects (LSP: Liskov Substitution Principle)
Clearly segregated interfaces (ISP: Interface Segregation Principle)
Be dependent on abstractions, instead of concrete classes, neither high nor low (DIP: Dependency Inversion Principle)
More Details and Implementations
Encapsulate what varies
Finding the vary behaviors, and building an interface for the main class to form a HAS-A Composition relationship.
Favor composition over inheritance
Composition is basically including a interface in the main class, and let the implementation of the interface to determine the behavior of the main class.
Loose coupling
The relationship between classes should not be concrete, and no class that use another class should know too much about it. Just use interface.
Program to interface
Segregating the part that is too concrete into another interface, and then implement the code behavior in the implementation of the interface.
Single Responsibility Principle
Each object class should only responsible for one behavior, which is good to check by cohesion check.
Open-closed principle
Using Strategy pattern to making IS-A relationships converted to HAS-A composition relationship, and using the behavior interface in the main class. Then we can implement each new behaviors for extension, and remove them easily. Eventually the Factory Method Pattern can help us to instantiate the concrete behavior class.
Liskov’s substation principle
Each sub class should not only for an IS-A relationships, but can also substitute the base/super class without unexpected behavior. The only way to check this is to check with Unit Tests, and check about the cohesion logics of the sub class and super classes.
Interface segregation principle
The interface should have clear separation of duty, and not combining unrelated items together to cause behavior pollution.
Dependency Inversion Princile
Both the high level and low level classes should depending on abstraction classes. The best way to understand this is to always extract the general behavior out from both super class and lower lever class: (higher level using composition of the abstract class, and the lower level implement the abstract class behaviors/functions). This guarantee that non of the class can be affected by the change of each other.