SOLID Principles
SOLID Principles
=======================
Pragy
Senior Software Engineer + Instructor @ Scaler
https://www.linkedin.com/in/agarwalpragy/
💎 Key Takeaways
================
🎧 Audio/Video issues
Disable Ad Blockers & VPN. Check your internet. Rejoin the session.
💡 Prerequisites?
Basics of Object Oriented Programming
-----------------------------------------------------------------------------
✅ Goals
========
>
> ❓ What % of your work time is spend writing new code?
>
> • 10-15% • 15-40% • 40-80% • > 80%
good time
- Breaks: play TT, play snooker, chai, ..
1. Readable
2. Extensible
3. Testable
4. Maintainable
===================
💎 SOLID Principles
===================
Inversion of Control
💭 Context
==========
I will use Java syntax highlighting - but the code that I write will NOT be
java
-----------------------------------------------------------------------------
🎨 Design a Character
======================
- visitors
- animals
- staff
```java
class ZooEntity {
// attributes - properties (data members)
// Staff
String name;
Gender gender;
Integer age;
Double salary;
String designation;
String department;
// ...
// Animal
String name;
Gender gender;
Integer age;
Boolean eatsMeat;
Boolean canFly;
// Visitor
String name;
Gender gender;
Ticket ticket;
Boolean isVIP;
DateTime timeOfArrival;
// methods - behavior
// Staff
void sleep();
void cleanPremises();
void eat();
void poop();
void feedAnimals();
// Animal
void sleep();
void eat();
void poop();
void fly();
void fight();
void eatTheVisitor();
// Visitor
void roamAround();
void eat();
void petAnimals();
void getEatenByAnimals();
}
class ZooEntityTester {
void testAnimalEats() {
ZooEntity simba = new ZooEntity(...);
simba.eat(...);
```
Minor Issue:
- name conflicts
- easy to fix: just rename the variables
- `name` => `staffName`, `visitorName`, `animalName`
❓ Readable
Yes, I can totally read & understand this code!
❓ Testable
❓ Extensible
❓ Maintainable
Deepto: Animals
Ajayraj: Staff
Sejal: Visitors
==================================
⭐ Single Responsibility Principle
==================================
```java
class ZooCharacter {
String name;
Integer age;
Gender gender;
void eat();
void poop();
void sleep();
}
void cleanPremises();
}
void roamAround();
}
void eatTheVisitor();
}
```
❓ Readable
❓ Testable
❓ Extensible
(for later)
❓ Maintainable
No.
-----------------------------------------------------------------------------
Towards the class end, I will give you assignments: to help you practice!
-----------------------------------------------------------------------------
🐦 Design a Bird
================
```java
void fly() {
}
}
```
```java
class Bird extends Animal {
// String species; // inherited
void fly() {
if (species == "sparrow")
print("Flaps wings quickly and flies low")
else if (species == "pigeon")
print("Fly over people's heads, and poop on them")
else if (species == "eagle")
print("Glide high above elegantly")
}
}
```
- Readable
- Testable
- Maintainable
- Extensible - FOCUS!
```java
[PublicZooLibary] {
class Animal {
String species;
}
[MyCustomGame] {
import PublicZooLibary.Animal;
import PublicZooLibary.Bird;
class MyZooGame {
void main() {
Bird tweety = new Bird("pigeon");
tweety.fly();
}
}
}
```
If I need to add a new type of bird - Peacock - how can I achieve this?
=======================
⭐ Open/Close Principle
=======================
- Developer
- write code on their laptop
- test it locally
- commit & push & generate a Pull Request (PR)
- PR will go for review
- other devs in the team will suggest improvements
- the dev will go and make changes to make those improvements
- they will re-raise the PR
- re-review
- (iterative process)
- PR gets merged
- Quality Assurance
- write extra unit tests
- write integration tests
- UI: manual testing
- update the docs
- Deployment Pipeline
+ Staging servers
- will ensure that the code doesn't break
- there will be a lot of testing (unit/integration) & stress-testing
+ Production servers
* A/B test
- deploy to only 5% of the users
- we will monitor a lot of metrics
- number of exception
- customer satisfaction
- number of purchases
- ...
* Deploy to 100% of the userbase
```java
[PublicZooLibary] {
class Animal {
String species;
}
[MyCustomGame] {
import PublicZooLibary.Animal;
import PublicZooLibary.Bird;
import PublicZooLibary.Sparrow;
class MyZooGame {
void main() {
Sparrow tweety = new Sparrow();
tweety.fly();
Peacock pea = new Peacock();
pea.fly();
}
}
}
```
OCP says: your code should be designed with extension in mind from the
first-day
Write your code in such a way that anyone can add new features, even if
they don't have modification access!
❔ Isn't this the same thing that we did for Single Responsibility as well?
🔗 All the SOLID principles are tighly linked together. If you implement one,
you might get others for free!
-----------------------------------------------------------------------------
## Salary
Google:
- massive projects: millions of lines of code
- 100+ devs on the same project
- projects lasts 10+ years
- devs join / devs leave
- different devs have different skills
- SOLID Principles
- Design Patterns
- Structural / Behavioral / Creational
- builder / singleton / factory / strategy / adapter / proxy / ...
- Case Studies
- Tic Tac Toe
- Chess
- Snake Ladder
- Parking Lot
- Splitwise
- Library Management
- REST API
- idempotency
- naming conventions
Builder pattern
- works around language limitations
- java does not have the following features
- named arguments
- change the position of arguments
- validation for arguments
- default values for arguments
- you use builder pattern in java to work around these issues
### Solution
Always have a mentor who know what the correct thing is and can guide you on
the correct thing
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
```java
Flightless Birds:
penguins, ostrich, emu, dodo, turkeys, ...
🏃♀️ Run away from the problem - Simply don't implement the `void fly()`
```java
abstract class Bird {
abstract void fly();
}
A subclass of abstract class should either implement all the abstract methods
(to complete the blueprint) or should itself be abstract (incomplete)
⚠️ Throw a proper exception!
```java
abstract class Bird {
abstract void fly();
}
🐞 Violates Expectations!
```java
abstract class Bird {
abstract void fly();
}
class ZooGame {
// Runtime Polymorphism
//
// (variable) (object)
// Bird b = new Sparrow()
// b: variable of type Bird
// obj: of type Sparrow
// can I store an object of type Sparrow in a variable of type Bird?
// YES!
// because a Sparrow is-a Bird
// anything that a Bird can do, a Sparrow can also do that
Bird getBirdObjectFromUserSelection() {
// shows a UI to the user
// shows the different bird types
// user selects a bird type
// create an object of that bird type
// return
/**
* if (userSelection == "Sparrow")
* return new Sparrow()
* else if (userSelection == "Eagle")
* return new Eagle()
*/
}
void main() {
Bird b = getBirdObjectFromUserSelection();
b.fly();
}
}
```
✅ Before extension
An intern (Pragy) comes to the company - and implements the class Kiwi
```java
class Kiwi extends Bird {
void fly() {
throw new FlightlessBirdException("Kiwi no fly bro")
}
}
```
❌ After extension
- Are you aware of the fact that someone has made this change?
Most likely no.
- Indian intuition:
- Parents set expectations: 'my kid will be a doctor!'
- Children: must not violate tghe expectations set by the parents
Another example
```java
class Formatter {
String format(Object obj) {
// format that object
}
}
```
```java
interface ICanFly {
void fly();
}
void fly() {
print("flap wings and fly low")
}
```
```java
interface ICanFly {
void fly();
}
class ZooGame {
Bird getBirdObjectFromUserSelection() {
// this method can return Kiwi as well
}
void main() {
Bird b = getBirdObjectFromUserSelection()
}
}
```
-----------------------------------------------------------------------------
```java
interface ICanFly {
void fly();
void flapWings();
void kickToTakeOff();
}
void flapWings() {
// SORRY Shaktiman!
}
}
```
>
> ❓ 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
==================================
```java
interface ICanFly {
void fly();
}
interface HasWings {
void flapWings();
}
interface HasLegs {
void kickToTakeOff();
}
```
interface DatabaseCursor {
List<Row> find(String query)
List<Row> insert(String query)
List<Row> delete(String query)
List<Row> join(String query, String table1, String table2)
}
```
-----------------------------------------------------------------------------
🗑️ Design a Cage
================
```java
class Cage1 {
// cage for tigers
List<Tiger> tigers;
public Cage1() {
this.tigers = Arrays.asList(
new Tiger("simba"),
new Tiger("musafa"),
)
}
void feedAnimals() {
for(Tiger t: this.tigers)
this.bowl.feed(t)
}
}
class Cage2 {
// cage for sparrows
List<Sparrow> spparows;
public Cage2() {
this.sparrows = Arrays.asList(
new Sparrow("tweety"),
new Sparrow("shweety"),
)
}
void feedAnimals() {
for(Sparrow s: this.sparrows)
this.bowl.feed(s)
}
}
class ZooGame {
void main() {
Cage1 tigerCage = new Cage1();
// what if I want to create a cage for Sparrows?
Cage sparrowCage = new Cage2();
}
}
```
🐞 Code repetition! I literally copy pasted the Cage1 class and made minor
changes!
```
```
=================================
⭐ Dependency Inversion Principle what we want
=================================
```
------- --------- -------
IBowl Animal IDoor High Level Abstraction
------- --------- -------
│ │ │
╰───────────────╁──────────────╯
┃
┏━━━━━━┓
┃ Cage ┃
┗━━━━━━┛
```
But how?
=======================
💉 Dependency Injection how to achieve that
=======================
```java
class Cage {
// Generic Cage
IDoor door; // depend on Abstraction instead of Implementation Detail
IBowl bowl;
List<Animal> animal;
void feedAnimals() {
for(Animal a: this.animals)
this.bowl.feed(a)
}
}
class ZooGame {
void main() {
Cage tigerCage = new Cage(
new IronDoor(...),
new MeatBowl(...),
Arrays.asList(
new Tiger("simba"),
new Tiger("musafa")
)
)
They do this for a good reason - so that things don't break when the
requirements change tomorrow
If you're a dev who is not good at LLD & you end up clearing Google
- but you will have a very hard time
- you will not be able to understand the code
- be put into a PIP (Performance improvement proposal)
================
🎁 Bonus Content
================
>
> We all need people who will give us feedback.
> That’s how we improve. 💬 Bill Gates
>
-------------
🧩 Assignment
-------------
https://github.com/kshitijmishra23/low-level-design-
concepts/tree/master/src/oops/SOLID/
----------------------
⭐ Interview Questions
----------------------
>
> ❓ 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
>