SOLID Principles
SOLID Principles
SOLID Principles
================
💬 Communicate using the chat box
❓ FAQ
======
▶️Will the recording be available?
To Scaler students only
🎧 Audio/Video issues
Disable Ad Blockers & VPN. Check your internet. Rejoin the session.
💡 Prerequisites?
Basics of Object Oriented Programming
Pragy
[linkedin.com/in/AgarwalPragy](https://www.linkedin.com/in/AgarwalPragy/)
-----------------------------------------------------------------------------
>
> ❓ What % of your work time is spend writing new code?
>
> • 10-15% • 15-40% • 40-80% • > 80%
>
< 15%
✅ Goals
========
1. Extensible
2. Maintainable
3. Testable
4. Readable
===================
💎 SOLID Principles
===================
- Single Responsibility
- Open Closed
- Liskov's Substitution (Barbara Liskov)
- Interface Segregation
- Depedency Inversion
💭 Context
==========
-----------------------------------------------------------------------------
🐶 Design an Animal
===================
```java
class Animal {
// ideas / concepts in our mind => classes in OOP
// properties / attributes
String name;
Float age;
Float height;
String species;
String gender;
Color color;
Float lifeExpectancy;
Integer numberOfLegs;
String foodType;
Boolean isWild;
Boolean canSwim;
// behavior / methods
void speak() {}
void eat() {}
void walk() {}
void fly() {}
void breed() {}
```
🐠 Design the Speaking behavior - different animals will speak in different manners
```java
class Animal {
String species;
// ...
void speak() {
}
}
class AnimalTester {
void testCatSpeak() {
Animal kitty = new Animal();
kitty.species = "cat";
kitty.speak() // assert that this prints "Nyan"
}
}
```
❓ Readable
I can defintely read this code & understand it.
If I have 100 species, then will I be able to understand this code? No
I have to carefully analyse each condition before claiming how the code behaves
❓ Testable
I can totally write testcases - is testable
Code is coupled - changing the behavior of 1 species might effect the behavior of another species without
intention
❓ Extensible
I can totally add a new else-if condition if I need to add a new species
Adding extra functionality is difficult
We will revisit this part later
❓ Maintainable
Multiple devs can work on this codebase
All working on the same file, same class, same function - merge conflicts
🛠️ How to fix this?
==================================
⭐ Single Responsibility Principle
==================================
- if you identify that there is a piece of code which is serving multiple responsibilities - break the code
down into smaller pieces
```java
```
- Readable
If we have 100 species, then we will now have 100 classes!
1. This code can be reduced by metaprogramming (generics, templates, macros, reflection, preprocessor,
decorators ...)
2. Each file is small and easy to read
- as a developer, you will be working with a handful of files at any given time
- Testable
Each of the species is testable separately. Code is now decoupled
- Maintainable
Different devs working on different species will not lead to merge conflicts
-----------------------------------------------------------------------------
🐦 Design a Bird
================
```java
class Animal {
String species;
}
```java
[library] MySimpleZoo {
// code provided by the above library
// executable - .com .dll .o .so .class .jar - source code is not available
// open source - it might be someone else's code, and you don't have the permission to modify it
class Animal {
String species;
}
if(species == "sparrow")
print("fly indoors")
else if (species == "eagle")
print("fly elegantly above the clouds")
else if (species == "penguin")
print("dude, I can't really fly")
}
}
import MySimpleZoo.Animal;
import MySimpleZoo.Bird;
// I (the user of the library code) wish to add new functionality to the Bird class - add a new species
"Duck"
class MyAwesomeZooGame {
void main() {
Bird b = new Bird("sparrow");
b.fly();
}
}
```
- Readable
- Testable
- Maintainable
- Extensible - FOCUS!
If I don't have modification permissions to some piece of code, I will not be able to add new functionality
to that code.
=======================
⭐ Open-Close Principle
=======================
- Code should be closed for modification, but still, open for extension!
```java
[library] MySimpleZoo {
// code provided by the above library
// executable - .com .dll .o .so .class .jar - source code is not available
// open source - it might be someone else's code, and you don't have the permission to modify it
class Animal {
String species;
}
import MySimpleZoo.*;
// I (the user of the library code) wish to add new functionality to the Bird class - add a new species
"Duck"
class MyAwesomeZooGame {
void main() {
Bird b = new Bird("sparrow");
b.fly();
}
}
```
- Extension
We can extend this code freely - without having to modify the library that we don't have the source-
code/edit-persmissions for!
❔ Didn't we do the same thing to fix the non-SRP and non-OC code?
Yes, certainly! Exact same resolution
❔ Does this mean that the Single Responsibility Principle (SRP) == Open-Close Principle (OCP)
No - the intention was different. The solution was same
-----------------------------------------------------------------------------
3 hour masterclass - code everything in the Low Level Design topics in this?
DSA/SQL/Networks/Memory/Concurrency/LLD/HLD/...
a success metric for an online upskilling course - before and after of the career
most measureable thing is $$$$$$$$
I will teach you for 11 months and after that you will feel really happy
-----------------------------------------------------------------------------
resuming class at 9.55 pm sharp!
-----------------------------------------------------------------------------
```java
```
Kiwi, Ostrich, Penguins, Dodo, Emu ... are all birds that can't fly
>
> ❓ How do we solve this?
>
> • Throw exception with a proper message
> • Don't implement the `fly()` method
> • Return `null`
> • Redesign the system
>
🏃♀️ Run away from the problem - don't implement the fly method!
```java
// void fly() {
// !? // kiwi doesn't fly!
// }
}
```
🐞 This will lead to a compiler error. Compiler says that either implement void fly inside class Kiwi, or
mark class Kiwi as abstract class too
⚠️Throw an exception
```java
```
```java
// Old Code
class ZooGame {
Bird getBirdFromUserChoice() {
// ask the user for the species of Bird that they want
// return an object of that Bird species
}
void main() {
Bird b = getBirdFromUserChoice();
b.fly(); // this works perfectly fine!
}
}
```
```java
// new code - extension
```
✅ Before extension
Code was working fine.
It was tested, it was performant, it was readable, it got shit done!
❌ After extension
I did not touch the old code - should the old code magically crash!? No.
I just added new code. But surprisingly, the OLD code crashes!
==================================
⭐ Liskov's Substitution Principle
==================================
- Any child `class C extends P` object should be able to replace any parent `class P` object without any
issues
- child classes can add new functionality, but they should not break existing functionality of the parent
class
```java
// abstract void fly() => not in this class - because not all birds can fly
}
```
-----------------------------------------------------------------------------
Doesn't this violate open-close?
Earlier code was
```java
abstract class Bird {
abstract void fly();
}
```
```java
abstract class Bird {
String species;
Float lengthOfBeak;
// abstract void fly() => not in this class - because not all birds can fly
}
-----------------------------------------------------------------------------
Your job is to anticipate / predict these future changes, and write the 2nd code from the very start
-----------------------------------------------------------------------------
```java
interface ICanFly {
void fly();
Bats, Aeroplane, Insects, Shaktiman, Kite (Patang), Drones, Imagination, Rumors, My mom chappal flies towards
me, Balloons
>
> ❓ Should these additional methods be part of the ICanFly interface?
>
> • Yes, obviously. All things methods are related to flying
> • Nope. [send your reason in the chat]
>
==================================
⭐ Interface Segregation Principle
==================================
- Keep your interfaces minimal - no class should be forced to implement methods it doesn't need
- If you find a cluttered interface - you break it into multiple smaller interfaces
```java
interface ICanFly {
void fly();
}
void kickToTakeOff();
void spreadWings();
}
"interfaces" are not just about class definitions - they are also about external interfaces - APIs
Rules vs Guidelines
===================
- Rules
+ these are enforced by someone
+ don't kill - enforced by the police
+ wear your seatbelt - enforced by the police
+ pay your taxes - enforced by the government (unless you're a politician)
- Guidelines
+ good to have - suggestions - take it or leave it - best practices
+ don't smoke - you can smoke if you want - it will very unhealthy
+ wear a mask - you can survive without a mask as well, but you're more like to die to Covid
+ walk to your left - INCORRECT
+ drive to your left
+ walk to your right
------------------------
=> - - - - =>
------------------------
=> - - - - <=
------------------------
-----------------------------------------------------------------------------
🗑️ Design a Cage
================
```java
// High level code => abstractions - it tells you what is there, but not how it works
// Low level code => implementation details - it tells you exactly how something works
public Cage1() {}
class Cage2 {
// cage for X-men
MeatBowl bowl = new MeatBowl();
AdamantiumDoor door = new AdamantiumDoor();
List<XMen> occupants = new ArrayList<>(
new Wolverine("kitty 1"),
new Deadpool("kitty 2")
);
public Cage2() {}
void feed() {
for(XMen t: this.occupants) {
if(this.bowl.isEmpty())
this.bowl.refill()
this.bowl.feed(t);
}
}
class MyAwesomeZooGame {
void main() {
Cage1 kittyCage = new Cage1();
kittyCage.feed();
kittyCage.handleAttack(new Attack(10));
```
```
------- --------- -------
IBowl IAnimal IDoor high level
------- --------- -------
║ ║ ║
║ ║ ║
┏━━━━━━━━━━┓ ┏━━━━━━━┓ ┏━━━━━━━━━━┓
┃ MeatBowl ┃ ┃ Tiger ┃ ┃ IronDoor ┃ low level
┗━━━━━━━━━━┛ ┗━━━━━━━┛ ┗━━━━━━━━━━┛
│ │ │
╰───────────────╁───────────────╯
┃
┏━━━━━━━┓
┃ Cage1 ┃ high level
┗━━━━━━━┛
```
High level class `Cage1` depends on low-level implementation details `MeatBowl`, ...
=================================
⭐ Dependency Inversion Principle
=================================
```
------- --------- -------
IBowl IAnimal IDoor
------- --------- -------
│ │ │
╰───────────────╁──────────────╯
┃
┏━━━━━━┓
┃ Cage ┃
┗━━━━━━┛
```
But how?
=======================
💉 Dependency Injection
=======================
- Inversion - principle
- Injection - how to achieve that
- Instead of creating your dependencies yourself, let your clients inject them.
```java
class MyAwesomeZooGame {
void main() {
Cage kittyCage = new Cage (
new MeatBowl(),
new IronDoor(),
Arrays.asList(
new Tiger(),
new Lion(),
)
);
}
}
```
Enterprise Code
===============
```java
class RazorPayPaymentGatewayBuilderFactory implement IPaymentGatewayBuilderFactory {
SimpleFileLogger logger = SimpleFileLogger.getInstance(RazorPayPaymentGatewayBuilderFactory.class);
}
```
- pattern everywhere
- strategies, factories, builders, factories of factories, factories of builders, builders of factories,
proxies, adapters, ...
- if you don't know LLD - even if you get into Google, you will have a hard time surviving there
- everytime you look at a piece of code, you would want to jump out of the window!
- if you know LLD, then you won't even have to read 95% of the code
- just looking at the class name will tell you exactly what the code does!
⏱️ Quick Recap
==============
================
🎁 Bonus Content
================
>
> We all need people who will give us feedback.
> That’s how we improve. 💬 Bill Gates
>
======================
⭐ Interview Questions
======================
> ❓
Which of the following is an example of breaking
> Dependency Inversion Principle?
>
> A) A high-level module that depends on a low-level module
> through an interface
>
> B) A high-level module that depends on a low-level module directly
>
> C) A low-level module that depends on a high-level module
> through an interface
>
> D) A low-level module that depends on a high-level module directly
>
>
> ❓ Which of the following is an example of breaking
> Liskov Substitution Principle?
>
> A) A subclass that overrides a method of its superclass and changes
> its signature
>
> B) A subclass that adds new methods
>
> C) A subclass that can be used in place of its superclass without
> any issues
>
> D) A subclass that can be reused without any issues
>
> ❓ How can we achieve the Interface Segregation Principle in our classes?
>
> A) By creating multiple interfaces for different groups of clients
> B) By creating one large interface for all clients
> C) By creating one small interface for all clients
> D) By creating one interface for each class
> ❓ Which SOLID principle states that a subclass should be able to replace
> its superclass without altering the correctness of the program?
>
> A) Single Responsibility Principle
> B) Open-Close Principle
> C) Liskov Substitution Principle
> D) Interface Segregation Principle
>
>
> ❓ How can we achieve the Open-Close Principle in our classes?
>
> A) By using inheritance
> B) By using composition
> C) By using polymorphism
> D) All of the above
>
=============================
⭐ How do we retain knowledge
=============================
>
> ❓ Do you ever feel like you know something but are unable to recall it?
>
> • Yes, happens all the time!
>
> • No. I'm a memory Jedi!
>
-------------
🧩 Assignment
-------------
https://github.com/kshitijmishra23/low-level-design-concepts/tree/master/src/oops/SOLID/