078. 1.1. LLD - 2
078. 1.1. LLD - 2
HW:
1. Refactor this code such that it does not violate the SRP principle.
2. Refactor this code such that it does not violate the LSP principle.
3. You are given the implementation for this business requirement here. Change it for the new
requirements.
LLD - 2
Java vs other languages
Recap
LLD
Programming Paradigms
Object Oriented
Functional
Procedural
Delcarative
Reactive
Event Based
Classes - Abstraction (hiding details) / entities / concepts
Attributes (member variables)
Behavior (methods)
method vs function -> methods are functions bound to an object
Relationships with other classes (is-a: inheritance, has-a: variables)
Objects - Snapshots / instance of a class
tangible
Class Inheritance
Abstract Classes
Incomplete blueprints
missing details - abstract methods
we can't intantiate them
extend and complete them
Interfaces
Abstract class vs Interface
interface has incompete methods only (old stuff)
no state in interface
A class can implement many interfaces, but cannot extend multiple classes
Runtime Polymorphism
Encapsulation - data hiding
Getter Setter
Data Validation
Runtime Polymorphism
class Person {
}
class Student extends Person {
}
class BrilliantStudent extends Student {
}
class Professor extends Person {
void foo() {
// a Person variable can store any object of Person or any child class
Person person = new Person();
Person person = new Student(); // each student is also a person
Person person = new Professor(); // each professor is also a person
}
function overloading
method overriding
Java - usually runtime
Rust - always compiletime
Today
SOLID Principles
guidelines that have been created for you - readable, extensible, maintabile
guidelines not rules
API - Application Programming Interface Website - backend server - frontend (browser) - expose some API endpoint
S - Single Responsility
A class / method / whatever -> 1 and only 1 task
doing more than 1 thing is bad
O - Open Close
Any code/class should be open for extension and closed for mofidication
customizing behavior should be possible by extending the code and not need direct modification
L - Liskov's Substitution
Runtime Polymorphism
Any child class-object should be substitutable for a parent-class object.
I - Interface Segregation
Anyone who needs an interface should not be forced to implement functions that it does not need
Interfaces should be minimal
D - Dependency Inversion
Code should not have hard dependencies
Your code
@Getter @Setter
private String symbol;
@Getter @Setter
private double converstionRateToUSD;
class Money {
@Getter @Setter
private Currency currency;
@Getter @Setter
private double amount;
client
class Client {
public void payment() {
Money request = new Money(Currency.INR, 100);
Money balance = new Money(Currency.EUR, 50);
// conversion
double normalizedBalance = balance.getAmount() * balance.getCurrency().getConversionRateToUSD();
double normalizedRequest = request.getAmount() * request.getCurrency().getConversionRateToUSD();
// checking
if(normalizedBalance >= normalizedRequest) {
// make payment
}
}
}
SRP
The client is asking the object for data (avoid this)
Always ask your objects to do stuff for you
class Money{
boolean isGreaterThanOrEqualTo(Money m2) {
// conversions
// returning a boolean
}
}
class MoneyComparisonUtils {
public static isGreaterThanOrEqualTo(Money m1, Money m2) {}
}
class Client {
public void payment() {
Money request = new Money(Currency.INR, 100);
Money balance = new Money(Currency.EUR, 50);
if(balance.isGreaterThanOrEqualTo(request)) {
// make payment
}
}
}
Class / Struct
A class can have encapsulation / data valiation / behaviour (methods) A struct just holds information / dataclass
// dataclass
class Point {
int x;
int y;
}
HR Management system
Full Time
Part Time
class Bird {
void fly(String birdType) {
if(birdType.equals("kiwi")) {
//
} else if (birdType.equals("hen")) {
//
}
}
}
class Bird {
abstract void fly();
}
class Hen extends Bird {
//
}
Bad code? because if we want to add a new bird type - change the already implemented code
More code vs Difficult Code - both are bad More code is preferrable to Difficult Code
void solve() {
// intiliaze an array of size 10
int ar[10];
// go over each iter
for(int i = 0; i < 10; i++) {
// input from stdin
ar[i] = Integer.parseInt(br.readLine());
}
// go over each item
for(int i = 0; i < 10; i++) {
// multiple by itself
ar[i] = ar[i] * ar[i];
}
ar = [i * i for i in ar]
map = {
key: value
}
class Bird {
abstract void fly() {
}
}
class client {
void foo(Bird bird) {
bird.fly();
}
}
get(int i)
set(int i, int val)
A stack CAN BE implemented as an array but A stack is NOT an array
interface CanFly {
void fly() {}
}
void foo() {
Bird b = new Kiwi();
b.fly(); // compiler error
}
// dependencies
abstract class Engine {}
class V8Engine extends Engine {}
class TurboChargedEngine extends Engine {}
// library code
class Car {
TurboChargedEngine engine;
SelfInflatingTyre backLeftTyre, backRightTyre, ..;
BullproofBody body;
public Car() {
}
}
void foo() {
Car normalCar = new Car(new NormalEngine(), new NormalTyre(), new PlasticBody());
Car sportsCar = new Car(new V8EngineEngine(), new SelfInflatingTyre(), new BulletProofBody());
}
class SavePlayerInformation {
DBConnection connection;
public void save(Player player) {
String serialized = player.toString();
connection.saveToTable("table-name", serialized);
}
}
class SavePlayerInformation {
Writable connection;
public SavePlayerInformation(Writable connection) {
this.connection = connection;
}
public void save(Player player) {
String serialized = player.toString();
connection.write(serialized);
}
}
Dependency Inversion - classes should only have relations to high-level abstractions Dependency Injection - setter method -
constructor - Spring also provides @autowired
Create a GUI library that allows you to create games - Game Engine
// Library
class GameEngine {
public void drawCharachter(Character c) {
// draw c on the screen
}
// Client
class Main {
public static void main(String args[]) {
Character characters[];
while(true) {
// wait for event
int keycode = gameEngine.getKeypress();
// handle event
// Framework
class GameEngine {
public Game() {
}
public abstract void handleKeypress(int keycode);
// Client
class MyGame extends Game {
public MyGame () {
}
public handleKeypress(int keycode) {
// custom logic here
}
public ArrayList<Character> getCharactersToDraw() {
}
}
Inversion of Control
class Bird {
}
class Weapon {
}
class FighterJet
composition has-a relationship (car has an engine) (variables) can-do relationship (fightjet can fly, fighterjet can attack)
(implementing interfaces / multiple inheritance)
The real world is not a tree. The real world is more of a DAG.
Liskov - child class object should be able to replace to parent class object
class List<T> {
public List() {}
abstract public int size();
}
class List<Integer> {
public List() {}
abstract public int size();
}
class List<Boolean> {
public List() {}
abstract public int size();
}
// client
List<Integer> something
Runtime polymophism
class Person{}
class Manager extends Person {}
class Employee extends Person {}
void foo(Employee val) {
void spam() {
Person p;
if(condition) {
p = new Employee();
} else {
p = new Manager();
}
foo(p);
}
type based dispactch -> runtime polymorphism (in most languages) Rust
compiler