├── Hackathon 01 └── Problem Statement.pdf ├── Lecture 01 └── Notes.pdf ├── Lecture 02 ├── C++ Code │ ├── Abstraction.cpp │ └── Encapsulation.cpp ├── Java Code │ ├── Abstraction.java │ └── Encapsulation.java └── Notes.pdf ├── Lecture 03 ├── C++ Code │ ├── DynamicPolymorphism.cpp │ ├── Inheritance.cpp │ ├── StaticAndDynamicPolymorphism.cpp │ └── StaticPolymorphism.cpp ├── Java Code │ ├── DynamicPolymorphism.java │ ├── Inheritance.java │ ├── StaticAndDynamicPolymorphism.java │ └── StaticPolymorphism.java └── notes.pdf ├── Lecture 04 └── notes.pdf ├── Lecture 05 ├── C++ Code │ ├── LSP │ │ ├── LSP_Violated.cpp │ │ ├── LSP_followed.cpp │ │ └── LSP_followed_wrongly.cpp │ ├── OCP │ │ ├── OCP_followed.cpp │ │ └── OCP_violated.cpp │ └── SRP │ │ ├── SRP_followed.cpp │ │ └── SRP_violated.cpp └── Java Code │ ├── LSP │ ├── LSPFollowed.java │ ├── LSPFollowedWrongly.java │ └── LSPViolated.java │ ├── OCP │ ├── OCPFollowed.java │ └── OCPViolated.java │ └── SRP │ ├── SRPFollowed.java │ └── SRPViolated.java ├── Lecture 06 ├── C++ Code │ ├── DIP │ │ ├── DIP_followed.cpp │ │ └── DIP_violated.cpp │ ├── ISP │ │ ├── ISP_followed.cpp │ │ └── ISP_violated.cpp │ └── LSP-Rules │ │ ├── MethodRules │ │ ├── PostConditions.cpp │ │ └── PreConditions.cpp │ │ ├── PropertiesRules │ │ ├── ClassInvariants.cpp │ │ └── HistoryConstraint.cpp │ │ └── SingatureRules │ │ ├── ExceptionRule.cpp │ │ ├── MethodArgumentRule.cpp │ │ └── ReturnTypeRule.cpp ├── Java Code │ ├── DIP │ │ ├── DIPFollowed.java │ │ └── DIPViolated.java │ ├── ISP │ │ ├── ISPFollowed.java │ │ └── ISPViolated.java │ └── LSP-Rules │ │ ├── MethodRules │ │ ├── PostConditions.java │ │ └── PreConditions.java │ │ ├── PropertiesRules │ │ ├── ClassInvariants.java │ │ └── HistoryConstraint.java │ │ └── SingatureRules │ │ ├── ExceptionRule.java │ │ ├── MethodArgumentRule.java │ │ └── ReturnTypeRule.java └── notes.pdf ├── Lecture 07 ├── C++ Code │ ├── BadDesign │ │ └── DocumentEditor.cpp │ └── GoodDesign │ │ └── DocumentEditor.cpp ├── Java Code │ ├── Bad Design │ │ └── DocumentEditorClient.java │ └── Good Design │ │ └── DocumentEditorClient.java └── standardUml.png ├── Lecture 08 ├── C++ Code │ └── StrategyDesignPattern.cpp ├── Java Code │ └── StrategyDesignPattern.java └── Standard UML.png ├── Lecture 09 ├── C++ Code │ ├── AbstractFactory.cpp │ ├── FactoryMethod.cpp │ └── SimpleFactory.cpp └── Java Code │ ├── AbstractFactory.java │ ├── FactoryMethod.java │ └── SimpleFactory.java ├── Lecture 10 ├── C++ Code │ ├── NoSingleton.cpp │ ├── SimpleSingleton.cpp │ ├── ThreadSafeDoubleLockingSingleton.cpp │ ├── ThreadSafeEagerSingleton.cpp │ └── ThreadSafeLockingSingleton.cpp └── Java Code │ ├── NoSingleton.java │ ├── SimpleSingleton.java │ ├── ThreadSafeDoubleLockingSingleton.java │ ├── ThreadSafeEagerSingleton.java │ └── ThreadSafeLockingSingleton.java ├── Lecture 11 ├── C++ Code │ └── Tomato │ │ ├── TomatoApp.h │ │ ├── factories │ │ ├── NowOrderFactory.h │ │ ├── OrderFactory.h │ │ └── ScheduledOrderFactory.h │ │ ├── folderStructure.txt │ │ ├── main.cpp │ │ ├── managers │ │ ├── OrderManager.h │ │ └── RestaurantManager.h │ │ ├── models │ │ ├── Cart.h │ │ ├── DeliveryOrder.h │ │ ├── MenuItem.h │ │ ├── Order.h │ │ ├── PickupOrder.h │ │ ├── Restaurant.h │ │ └── User.h │ │ ├── services │ │ └── NotificationService.h │ │ ├── strategies │ │ ├── CreditCartPaymentStrategy.h │ │ ├── PaymentStrategy.h │ │ └── UpiPaymentStrategy.h │ │ └── utils │ │ └── TimeUtils.h ├── Java Code │ └── Tomato │ │ ├── Main.java │ │ ├── TomatoApp.java │ │ ├── factories │ │ ├── NowOrderFactory.java │ │ ├── OrderFactory.java │ │ └── ScheduledOrderFactory.java │ │ ├── folderStructure.txt │ │ ├── managers │ │ ├── OrderManager.java │ │ └── RestaurantManager.java │ │ ├── models │ │ ├── Cart.java │ │ ├── DeliveryOrder.java │ │ ├── MenuItem.java │ │ ├── Order.java │ │ ├── PickupOrder.java │ │ ├── Restaurant.java │ │ └── User.java │ │ ├── services │ │ └── NotificationService.java │ │ ├── strategies │ │ ├── CreditCardPaymentStrategy.java │ │ ├── PaymentStrategy.java │ │ └── UpiPaymentStrategy.java │ │ └── utils │ │ └── TimeUtils.java └── UML.png ├── Lecture 12 ├── C++ Code │ └── ObserverDesignPattern.cpp ├── Java Code │ └── ObserverDesignPattern.java └── Standard UML.jpg ├── Lecture 13 ├── C++ Code │ └── DecoratorPattern.cpp ├── Java Code │ └── DecoratorPattern.java └── Standard UML.jpeg ├── Lecture 14 ├── C++ Code │ ├── NotificationSystem.cpp │ └── NotificationSystemUpdated.cpp ├── Java Code │ ├── NotificationSystem.java │ └── NotificationSystemUpdated.java └── UML.jpeg ├── Lecture 15 ├── C++ Code │ └── CommandPattern.cpp ├── Example UML.jpg ├── Java Code │ └── CommandPattern.java └── Standard UML.jpg ├── Lecture 16 ├── C++ Code │ └── AdpaterPattern.cpp ├── Example UML.jpeg ├── Java Code │ └── AdapterPattern.java └── Standard UML.jpeg ├── Lecture 17 ├── C++ Code │ └── FacadePattern.cpp ├── Example UML.jpeg ├── Java Code │ └── FacadePattern.java └── Standard UML.jpeg ├── Lecture 18 ├── C++ Code │ └── MusicPlayerSystem │ │ └── MusicPlayerApplication │ │ ├── MusicPlayerApplication.hpp │ │ ├── MusicPlayerFacade.hpp │ │ ├── core │ │ └── AudioEngine.hpp │ │ ├── device │ │ ├── BluetoothSpeakerAdapter.hpp │ │ ├── HeadphonesAdapter.hpp │ │ ├── IAudioOutputDevice.hpp │ │ └── WiredSpeakerAdapter.hpp │ │ ├── enums │ │ ├── DeviceType.hpp │ │ └── PlayStrategyType.hpp │ │ ├── external │ │ ├── BluetoothSpeakerAPI.hpp │ │ ├── HeadphonesAPI.hpp │ │ └── WiredSpeakerAPI.hpp │ │ ├── factories │ │ └── DeviceFactory.hpp │ │ ├── folderstructure.txt │ │ ├── main.cpp │ │ ├── managers │ │ ├── DeviceManager.hpp │ │ ├── PlaylistManager.hpp │ │ └── StrategyManager.hpp │ │ ├── models │ │ ├── Playlist.hpp │ │ └── Song.hpp │ │ └── strategies │ │ ├── CustomQueueStrategy.hpp │ │ ├── PlayStrategy.hpp │ │ ├── RandomPlayStrategy.hpp │ │ └── SequentialPlayStrategy.hpp ├── Java Code │ └── MusicPlayerSystem │ │ └── MusicPlayerApplication │ │ ├── Main.java │ │ ├── MusicPlayerApplication.java │ │ ├── MusicPlayerFacade.java │ │ ├── core │ │ └── AudioEngine.java │ │ ├── device │ │ ├── BluetoothSpeakerAdapter.java │ │ ├── HeadphonesAdapter.java │ │ ├── IAudioOutputDevice.java │ │ └── WiredSpeakerAdapter.java │ │ ├── enums │ │ ├── DeviceType.java │ │ └── PlayStrategyType.java │ │ ├── external │ │ ├── BluetoothSpeakerAPI.java │ │ ├── HeadphonesAPI.java │ │ └── WiredSpeakerAPI.java │ │ ├── factories │ │ └── DeviceFactory.java │ │ ├── folderStructure.txt │ │ ├── managers │ │ ├── DeviceManager.java │ │ ├── PlaylistManager.java │ │ └── StrategyManager.java │ │ ├── models │ │ ├── Playlist.java │ │ └── Song.java │ │ └── strategies │ │ ├── CustomQueueStrategy.java │ │ ├── PlayStrategy.java │ │ ├── RandomPlayStrategy.java │ │ └── SequentialPlayStrategy.java └── UML.pdf ├── Lecture 19 ├── C++ Code │ └── CompositePattern.cpp ├── Example UML.jpeg ├── Java Code │ └── CompositePattern.java └── Standard UML.jpeg ├── PracticeProblems ├── PracticeProblem1.pdf └── PracticeProblem2.pdf └── README.md /Hackathon 01/Problem Statement.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Hackathon 01/Problem Statement.pdf -------------------------------------------------------------------------------- /Lecture 01/Notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 01/Notes.pdf -------------------------------------------------------------------------------- /Lecture 02/Java Code/Encapsulation.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Encapsulation says 2 things: 4 | 1. An Object's Characteristics and its behaviour are encapsulated together 5 | within that Object. 6 | 2. All the characteristics or behaviours are not for everyone to access. 7 | Object should provide data security. 8 | 9 | We follow above 2 pointers about Object of real world in programming by: 10 | 1. Creating a class that act as a blueprint for Object creation. Class contain 11 | all the characteristics (class variable) and behaviour (class methods) in one block, 12 | encapsulating it together. 13 | 2. We introduce access modifiers (public, private, protected, default) etc to provide data 14 | security to the class members. 15 | */ 16 | class SportsCar { 17 | private String brand; 18 | private String model; 19 | private boolean isEngineOn = false; 20 | private int currentSpeed = 0; 21 | private int currentGear = 0; 22 | 23 | //Introduce new variable to exaplain setters 24 | private String tyreCompany; 25 | 26 | public SportsCar(String brand, String model) { 27 | this.brand = brand; 28 | this.model = model; 29 | } 30 | 31 | public int getSpeed() { 32 | return currentSpeed; 33 | } 34 | 35 | public String getTyreCompany() { 36 | return tyreCompany; 37 | } 38 | 39 | public void setTyreCompany(String tyreCompany) { 40 | this.tyreCompany = tyreCompany; 41 | } 42 | 43 | public void startEngine() { 44 | isEngineOn = true; 45 | System.out.println(brand + " " + model + " : Engine starts with a roar!"); 46 | } 47 | 48 | public void shiftGear(int gear) { 49 | this.currentGear = gear; 50 | System.out.println(brand + " " + model + " : Shifted to gear " + currentGear); 51 | } 52 | 53 | public void accelerate() { 54 | if (!isEngineOn) { 55 | System.out.println(brand + " " + model + " : Engine is off! Cannot accelerate."); 56 | return; 57 | } 58 | currentSpeed += 20; 59 | System.out.println(brand + " " + model + " : Accelerating to " + currentSpeed + " km/h"); 60 | } 61 | 62 | public void brake() { 63 | currentSpeed -= 20; 64 | if (currentSpeed < 0) currentSpeed = 0; 65 | System.out.println(brand + " " + model + " : Braking! Speed is now " + currentSpeed + " km/h"); 66 | } 67 | 68 | public void stopEngine() { 69 | isEngineOn = false; 70 | currentGear = 0; 71 | currentSpeed = 0; 72 | System.out.println(brand + " " + model + " : Engine turned off."); 73 | } 74 | } 75 | 76 | //Main Method 77 | public class Encapsulation { 78 | public static void main(String[] args) { 79 | 80 | SportsCar mySportsCar = new SportsCar("Ford", "Mustang"); 81 | 82 | mySportsCar.startEngine(); 83 | mySportsCar.shiftGear(1); 84 | mySportsCar.accelerate(); 85 | mySportsCar.shiftGear(2); 86 | mySportsCar.accelerate(); 87 | mySportsCar.brake(); 88 | mySportsCar.stopEngine(); 89 | 90 | //Setting arbitrary value to speed. 91 | //mySportsCar.currentSpeed = 500; 92 | 93 | // System.out.println("Current Speed of My Sports Car is set to " + mySportsCar.currentSpeed); 94 | 95 | System.out.println("Current Speed of My Sports Car is " + mySportsCar.getSpeed()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Lecture 02/Notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 02/Notes.pdf -------------------------------------------------------------------------------- /Lecture 03/C++ Code/StaticPolymorphism.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | /* 7 | Static Polymorphism (Compile-time polymorphism) in real life says that 8 | the same action can behave differently depending on the input parameters. 9 | For example, a Manual car can accelerate by a fixed amount or by a 10 | specific amount you request. In programming, we achieve this via method 11 | overloading: multiple methods with the same name but different signatures. 12 | */ 13 | 14 | class ManualCar { 15 | 16 | private: 17 | string brand; 18 | string model; 19 | bool isEngineOn; 20 | int currentSpeed; 21 | int currentGear; 22 | 23 | public: 24 | ManualCar(string brand, string model) { 25 | this->brand = brand; 26 | this->model = model; 27 | this->isEngineOn = false; 28 | this->currentSpeed = 0; 29 | this->currentGear = 0; 30 | } 31 | 32 | void startEngine() { 33 | isEngineOn = true; 34 | cout << brand << " " << model << " : Engine started." << endl; 35 | } 36 | 37 | void stopEngine() { 38 | isEngineOn = false; 39 | currentSpeed = 0; 40 | cout << brand << " " << model << " : Engine turned off." << endl; 41 | } 42 | 43 | // Overloading accelerate - Static Polymorphism 44 | void accelerate() { 45 | if (!isEngineOn) { 46 | cout << brand << " " << model << " : Cannot accelerate! Engine is off." << endl; 47 | return; 48 | } 49 | currentSpeed += 20; 50 | cout << brand << " " << model << " : Accelerating to " << currentSpeed << " km/h" << endl; 51 | } 52 | 53 | void accelerate(int speed) { 54 | if (!isEngineOn) { 55 | cout << brand << " " << model << " : Cannot accelerate! Engine is off." << endl; 56 | return; 57 | } 58 | currentSpeed += speed; 59 | cout << brand << " " << model << " : Accelerating to " << currentSpeed << " km/h" << endl; 60 | } 61 | 62 | void brake() { 63 | currentSpeed -= 20; 64 | if (currentSpeed < 0) currentSpeed = 0; 65 | cout << brand << " " << model << " : Braking! Speed is now " << currentSpeed << " km/h" << endl; 66 | } 67 | 68 | void shiftGear(int gear) { 69 | currentGear = gear; 70 | cout << brand << " " << model << " : Shifted to gear " << currentGear << endl; 71 | } 72 | }; 73 | 74 | // Main function 75 | int main() { 76 | ManualCar* myManualCar = new ManualCar("Suzuki", "WagonR"); 77 | myManualCar->startEngine(); 78 | myManualCar->accelerate(); 79 | myManualCar->accelerate(40); 80 | myManualCar->brake(); 81 | myManualCar->stopEngine(); 82 | 83 | // Cleanup 84 | delete myManualCar; 85 | 86 | return 0; 87 | } -------------------------------------------------------------------------------- /Lecture 03/Java Code/StaticPolymorphism.java: -------------------------------------------------------------------------------- 1 | /* 2 | Static Polymorphism (Compile-time polymorphism) in real life says that 3 | the same action can behave differently depending on the input parameters. 4 | For example, a Manual car can accelerate by a fixed amount or by a 5 | specific amount you request. In programming, we achieve this via method 6 | overloading: multiple methods with the same name but different signatures. 7 | */ 8 | 9 | class ManualCar { 10 | private String brand; 11 | private String model; 12 | private boolean isEngineOn; 13 | private int currentSpeed; 14 | private int currentGear; 15 | 16 | public ManualCar(String brand, String model) { 17 | this.brand = brand; 18 | this.model = model; 19 | this.isEngineOn = false; 20 | this.currentSpeed = 0; 21 | this.currentGear = 0; 22 | } 23 | 24 | public void startEngine() { 25 | isEngineOn = true; 26 | System.out.println(brand + " " + model + " : Engine started."); 27 | } 28 | 29 | public void stopEngine() { 30 | isEngineOn = false; 31 | currentSpeed = 0; 32 | System.out.println(brand + " " + model + " : Engine turned off."); 33 | } 34 | 35 | // Overloading accelerate - Static Polymorphism 36 | public void accelerate() { 37 | if (!isEngineOn) { 38 | System.out.println(brand + " " + model + " : Cannot accelerate! Engine is off."); 39 | return; 40 | } 41 | currentSpeed += 20; 42 | System.out.println(brand + " " + model + " : Accelerating to " + currentSpeed + " km/h"); 43 | } 44 | 45 | public void accelerate(int speed) { 46 | if (!isEngineOn) { 47 | System.out.println(brand + " " + model + " : Cannot accelerate! Engine is off."); 48 | return; 49 | } 50 | currentSpeed += speed; 51 | System.out.println(brand + " " + model + " : Accelerating to " + currentSpeed + " km/h"); 52 | } 53 | 54 | public void brake() { 55 | currentSpeed -= 20; 56 | if (currentSpeed < 0) { 57 | currentSpeed = 0; 58 | } 59 | System.out.println(brand + " " + model + " : Braking! Speed is now " 60 | + currentSpeed + " km/h"); 61 | } 62 | 63 | public void shiftGear(int gear) { 64 | currentGear = gear; 65 | System.out.println(brand + " " + model + " : Shifted to gear " + currentGear); 66 | } 67 | } 68 | 69 | public class StaticPolymorphism { 70 | public static void main(String[] args) { 71 | ManualCar myManualCar = new ManualCar("Suzuki", "WagonR"); 72 | myManualCar.startEngine(); 73 | myManualCar.accelerate(); 74 | myManualCar.accelerate(40); 75 | myManualCar.brake(); 76 | myManualCar.stopEngine(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Lecture 03/notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 03/notes.pdf -------------------------------------------------------------------------------- /Lecture 04/notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 04/notes.pdf -------------------------------------------------------------------------------- /Lecture 05/C++ Code/LSP/LSP_Violated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Account { 5 | public: 6 | virtual void deposit(double amount) = 0; 7 | virtual void withdraw(double amount) = 0; 8 | }; 9 | 10 | class SavingAccount : public Account { 11 | private: 12 | double balance; 13 | 14 | public: 15 | SavingAccount() { 16 | balance = 0; 17 | } 18 | 19 | void deposit(double amount) { 20 | balance += amount; 21 | cout << "Deposited: " << amount << " in Savings Account. New Balance: " << balance << endl; 22 | } 23 | 24 | void withdraw(double amount) { 25 | if (balance >= amount) { 26 | balance -= amount; 27 | cout << "Withdrawn: " << amount << " from Savings Account. New Balance: " << balance << endl; 28 | } else { 29 | cout << "Insufficient funds in Savings Account!\n"; 30 | } 31 | } 32 | }; 33 | 34 | class CurrentAccount : public Account { 35 | private: 36 | double balance; 37 | 38 | public: 39 | CurrentAccount() { 40 | balance = 0; 41 | } 42 | 43 | void deposit(double amount) { 44 | balance += amount; 45 | cout << "Deposited: " << amount << " in Current Account. New Balance: " << balance << endl; 46 | } 47 | 48 | void withdraw(double amount) { 49 | if (balance >= amount) { 50 | balance -= amount; 51 | cout << "Withdrawn: " << amount << " from Current Account. New Balance: " << balance << endl; 52 | } else { 53 | cout << "Insufficient funds in Current Account!\n"; 54 | } 55 | } 56 | }; 57 | 58 | class FixedTermAccount : public Account { 59 | private: 60 | double balance; 61 | 62 | public: 63 | FixedTermAccount() { 64 | balance = 0; 65 | } 66 | 67 | void deposit(double amount) { 68 | balance += amount; 69 | cout << "Deposited: " << amount << " in Fixed Term Account. New Balance: " << balance << endl; 70 | } 71 | 72 | void withdraw(double amount) { 73 | throw logic_error("Withdrawal not allowed in Fixed Term Account!"); 74 | } 75 | }; 76 | 77 | class BankClient { 78 | private: 79 | vector accounts; 80 | 81 | public: 82 | BankClient(vector accounts) { 83 | this->accounts = accounts; 84 | } 85 | 86 | void processTransactions() { 87 | for (Account* acc : accounts) { 88 | acc->deposit(1000); //All accounts allow deposits 89 | 90 | //Assuming all accounts support withdrawal (LSP Violation) 91 | try { 92 | acc->withdraw(500); 93 | } catch (const logic_error& e) { 94 | cout << "Exception: " << e.what() << endl; 95 | } 96 | } 97 | } 98 | }; 99 | 100 | int main() { 101 | vector accounts; 102 | accounts.push_back(new SavingAccount()); 103 | accounts.push_back(new CurrentAccount()); 104 | accounts.push_back(new FixedTermAccount()); 105 | 106 | BankClient* client = new BankClient(accounts); 107 | client->processTransactions(); // Throws exception when withdrawing from FixedTermAccount 108 | 109 | return 0; 110 | } -------------------------------------------------------------------------------- /Lecture 05/C++ Code/LSP/LSP_followed_wrongly.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | class Account { 9 | public: 10 | virtual void deposit(double amount) = 0; 11 | virtual void withdraw(double amount) = 0; 12 | }; 13 | 14 | class SavingAccount : public Account { 15 | private: 16 | double balance; 17 | 18 | public: 19 | SavingAccount() { 20 | balance = 0; 21 | } 22 | 23 | void deposit(double amount) { 24 | balance += amount; 25 | cout << "Deposited: " << amount << " in Savings Account. New Balance: " << balance << endl; 26 | } 27 | 28 | void withdraw(double amount) { 29 | if (balance >= amount) { 30 | balance -= amount; 31 | cout << "Withdrawn: " << amount << " from Savings Account. New Balance: " << balance << endl; 32 | } else { 33 | cout << "Insufficient funds in Savings Account!\n"; 34 | } 35 | } 36 | }; 37 | 38 | class CurrentAccount : public Account { 39 | private: 40 | double balance; 41 | 42 | public: 43 | CurrentAccount() { 44 | balance = 0; 45 | } 46 | 47 | void deposit(double amount) { 48 | balance += amount; 49 | cout << "Deposited: " << amount << " in Current Account. New Balance: " << balance << endl; 50 | } 51 | 52 | void withdraw(double amount) { 53 | if (balance >= amount) { 54 | balance -= amount; 55 | cout << "Withdrawn: " << amount << " from Current Account. New Balance: " << balance << endl; 56 | } else { 57 | cout << "Insufficient funds in Current Account!\n"; 58 | } 59 | } 60 | }; 61 | 62 | class FixedTermAccount : public Account { 63 | private: 64 | double balance; 65 | 66 | public: 67 | FixedTermAccount() { 68 | balance = 0; 69 | } 70 | 71 | void deposit(double amount) { 72 | balance += amount; 73 | cout << "Deposited: " << amount << " in Fixed Term Account. New Balance: " << balance << endl; 74 | } 75 | 76 | void withdraw(double amount) { 77 | throw logic_error("Withdrawal not allowed in Fixed Term Account!"); 78 | } 79 | }; 80 | 81 | //Client class 82 | class BankClient { 83 | private: 84 | vector accounts; 85 | 86 | public: 87 | BankClient(vector accounts) { 88 | this->accounts = accounts; 89 | } 90 | 91 | void processTransactions() { 92 | for (Account* acc : accounts) { 93 | acc->deposit(1000); 94 | 95 | //Checking account type explicitly 96 | if (typeid(*acc) == typeid(FixedTermAccount)) { 97 | cout << "Skipping withdrawal for Fixed Term Account.\n"; 98 | } else { 99 | try { 100 | acc->withdraw(500); 101 | } catch (const logic_error& e) { 102 | cout << "Exception: " << e.what() << endl; 103 | } 104 | } 105 | } 106 | } 107 | }; 108 | 109 | int main() { 110 | vector accounts; 111 | accounts.push_back(new SavingAccount()); 112 | accounts.push_back(new CurrentAccount()); 113 | accounts.push_back(new FixedTermAccount()); 114 | 115 | BankClient* client = new BankClient(accounts); 116 | client->processTransactions(); 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /Lecture 05/C++ Code/OCP/OCP_followed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Product class representing any item in eCommerce. 7 | class Product { 8 | public: 9 | string name; 10 | double price; 11 | 12 | Product(string name, double price) { 13 | this->name = name; 14 | this->price = price; 15 | } 16 | }; 17 | 18 | //1. ShoppingCart: Only responsible for Cart related business logic. 19 | class ShoppingCart { 20 | private: 21 | vector products; // Store heap-allocated products 22 | 23 | public: 24 | void addProduct(Product* p) { 25 | products.push_back(p); 26 | } 27 | 28 | const vector& getProducts() { 29 | return products; 30 | } 31 | 32 | //Calculates total price in cart. 33 | double calculateTotal() { 34 | double total = 0; 35 | for (auto p : products) { 36 | total += p->price; 37 | } 38 | return total; 39 | } 40 | }; 41 | 42 | // 2. ShoppingCartPrinter: Only responsible for printing invoices 43 | class ShoppingCartPrinter { 44 | private: 45 | ShoppingCart* cart; 46 | 47 | public: 48 | ShoppingCartPrinter(ShoppingCart* cart) { 49 | this->cart = cart; 50 | } 51 | 52 | void printInvoice() { 53 | cout << "Shopping Cart Invoice:\n"; 54 | for (auto p : cart->getProducts()) { 55 | cout << p->name << " - Rs " << p->price << endl; 56 | } 57 | cout << "Total: Rs " << cart->calculateTotal() << endl; 58 | } 59 | }; 60 | 61 | //Abstract class 62 | class Persistence { 63 | private: 64 | ShoppingCart* cart; 65 | 66 | public: 67 | virtual void save(ShoppingCart* cart) = 0; // Pure virtual function 68 | }; 69 | 70 | class SQLPersistence : public Persistence { 71 | public: 72 | void save(ShoppingCart* cart) override { 73 | cout << "Saving shopping cart to SQL DB..." << endl; 74 | } 75 | }; 76 | 77 | class MongoPersistence : public Persistence { 78 | public: 79 | void save(ShoppingCart* cart) override { 80 | cout << "Saving shopping cart to MongoDB..." << endl; 81 | } 82 | }; 83 | 84 | class FilePersistence : public Persistence { 85 | public: 86 | void save(ShoppingCart* cart) override { 87 | cout << "Saving shopping cart to a file..." << endl; 88 | } 89 | }; 90 | 91 | int main() { 92 | ShoppingCart* cart = new ShoppingCart(); 93 | cart->addProduct(new Product("Laptop", 50000)); 94 | cart->addProduct(new Product("Mouse", 2000)); 95 | 96 | ShoppingCartPrinter* printer = new ShoppingCartPrinter(cart); 97 | printer->printInvoice(); 98 | 99 | Persistence* db = new SQLPersistence(); 100 | Persistence* mongo = new MongoPersistence(); 101 | Persistence* file = new FilePersistence(); 102 | 103 | db->save(cart); // Save to SQL database 104 | mongo->save(cart); // Save to MongoDB 105 | file->save(cart); // Save to File 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /Lecture 05/C++ Code/OCP/OCP_violated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Product class representing any item in eCommerce. 7 | class Product { 8 | public: 9 | string name; 10 | double price; 11 | 12 | Product(string name, double price) { 13 | this->name = name; 14 | this->price = price; 15 | } 16 | }; 17 | 18 | //1. ShoppingCart: Only responsible for Cart related business logic. 19 | class ShoppingCart { 20 | private: 21 | vector products; 22 | 23 | public: 24 | void addProduct(Product* p) { 25 | products.push_back(p); 26 | } 27 | 28 | const vector& getProducts() { 29 | return products; 30 | } 31 | 32 | //Calculates total price in cart. 33 | double calculateTotal() { 34 | double total = 0; 35 | for (auto p : products) { 36 | total += p->price; 37 | } 38 | return total; 39 | } 40 | }; 41 | 42 | // 2. ShoppingCartPrinter: Only responsible for printing invoices 43 | class ShoppingCartPrinter { 44 | private: 45 | ShoppingCart* cart; 46 | 47 | public: 48 | ShoppingCartPrinter(ShoppingCart* cart) { 49 | this->cart = cart; 50 | } 51 | 52 | void printInvoice() { 53 | cout << "Shopping Cart Invoice:\n"; 54 | for (auto p : cart->getProducts()) { 55 | cout << p->name << " - Rs " << p->price << endl; 56 | } 57 | cout << "Total: Rs " << cart->calculateTotal() << endl; 58 | } 59 | }; 60 | 61 | // 3. ShoppingCartStorage: Only responsible for saving cart to DB 62 | class ShoppingCartStorage { 63 | private: 64 | ShoppingCart* cart; 65 | 66 | public: 67 | ShoppingCartStorage(ShoppingCart* cart) { 68 | this->cart = cart; 69 | } 70 | 71 | void saveToSQLDatabase() { 72 | cout << "Saving shopping cart to SQL DB..." << endl; 73 | } 74 | 75 | void saveToMongoDatabase() { 76 | cout << "Saving shopping cart to Mongo DB..." << endl; 77 | } 78 | 79 | void saveToFile() { 80 | cout << "Saving shopping cart to File..." << endl; 81 | } 82 | }; 83 | 84 | int main() { 85 | ShoppingCart* cart = new ShoppingCart(); 86 | 87 | cart->addProduct(new Product("Laptop", 50000)); 88 | cart->addProduct(new Product("Mouse", 2000)); 89 | 90 | ShoppingCartPrinter* printer = new ShoppingCartPrinter(cart); 91 | printer->printInvoice(); 92 | 93 | ShoppingCartStorage* db = new ShoppingCartStorage(cart); 94 | db->saveToSQLDatabase(); 95 | 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /Lecture 05/C++ Code/SRP/SRP_followed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Product class representing any item in eCommerce. 7 | class Product { 8 | public: 9 | string name; 10 | double price; 11 | 12 | Product(string name, double price) { 13 | this->name = name; 14 | this->price = price; 15 | } 16 | }; 17 | 18 | //1. ShoppingCart: Only responsible for Cart related business logic. 19 | class ShoppingCart { 20 | private: 21 | vector products; // Store heap-allocated products 22 | 23 | public: 24 | void addProduct(Product* p) { 25 | products.push_back(p); 26 | } 27 | 28 | const vector& getProducts() { 29 | return products; 30 | } 31 | 32 | //Calculates total price in cart. 33 | double calculateTotal() { 34 | double total = 0; 35 | for (auto p : products) { 36 | total += p->price; 37 | } 38 | return total; 39 | } 40 | }; 41 | 42 | // 2. ShoppingCartPrinter: Only responsible for printing invoices 43 | class ShoppingCartPrinter { 44 | private: 45 | ShoppingCart* cart; 46 | 47 | public: 48 | ShoppingCartPrinter(ShoppingCart* cart) { 49 | this->cart = cart; 50 | } 51 | 52 | void printInvoice() { 53 | cout << "Shopping Cart Invoice:\n"; 54 | for (auto p : cart->getProducts()) { 55 | cout << p->name << " - Rs " << p->price << endl; 56 | } 57 | cout << "Total: Rs " << cart->calculateTotal() << endl; 58 | } 59 | }; 60 | 61 | // 3. ShoppingCartStorage: Only responsible for saving cart to DB 62 | class ShoppingCartStorage { 63 | private: 64 | ShoppingCart* cart; 65 | 66 | public: 67 | ShoppingCartStorage(ShoppingCart* cart) { 68 | this->cart = cart; 69 | } 70 | 71 | void saveToDatabase() { 72 | cout << "Saving shopping cart to database..." << endl; 73 | } 74 | }; 75 | 76 | int main() { 77 | ShoppingCart* cart = new ShoppingCart(); 78 | 79 | cart->addProduct(new Product("Laptop", 50000)); 80 | cart->addProduct(new Product("Mouse", 2000)); 81 | 82 | ShoppingCartPrinter* printer = new ShoppingCartPrinter(cart); 83 | printer->printInvoice(); 84 | 85 | ShoppingCartStorage* db = new ShoppingCartStorage(cart); 86 | db->saveToDatabase(); 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /Lecture 05/C++ Code/SRP/SRP_violated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Product class representing any item of any ECommerce. 7 | class Product { 8 | public: 9 | string name; 10 | double price; 11 | 12 | Product(string name, double price) { 13 | this->name = name; 14 | this->price = price; 15 | } 16 | }; 17 | 18 | // Violating SRP: ShoppingCart is handling multiple responsibilities 19 | class ShoppingCart { 20 | private: 21 | vector products; 22 | 23 | public: 24 | void addProduct(Product* p) { 25 | products.push_back(p); 26 | } 27 | 28 | const vector& getProducts() { 29 | return products; 30 | } 31 | 32 | // 1. Calculates total price in cart. 33 | double calculateTotal() { 34 | double total = 0; 35 | for (auto p : products) { 36 | total += p->price; 37 | } 38 | return total; 39 | } 40 | 41 | // 2. Violating SRP - Prints invoice (Should be in a separate class) 42 | void printInvoice() { 43 | cout << "Shopping Cart Invoice:\n"; 44 | for (auto p : products) { 45 | cout << p->name << " - Rs " << p->price << endl; 46 | } 47 | cout << "Total: Rs " << calculateTotal() << endl; 48 | } 49 | 50 | // 3. Violating SRP - Saves to DB (Should be in a separate class) 51 | void saveToDatabase() { 52 | cout << "Saving shopping cart to database..." << endl; 53 | } 54 | }; 55 | 56 | int main() { 57 | ShoppingCart* cart = new ShoppingCart(); 58 | 59 | cart->addProduct(new Product("Laptop", 50000)); 60 | cart->addProduct(new Product("Mouse", 2000)); 61 | 62 | cart->printInvoice(); 63 | cart->saveToDatabase(); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /Lecture 05/Java Code/LSP/LSPViolated.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | // Account interface 5 | interface Account { 6 | void deposit(double amount); 7 | void withdraw(double amount); 8 | } 9 | 10 | class SavingAccount implements Account { 11 | private double balance; 12 | 13 | public SavingAccount() { 14 | balance = 0; 15 | } 16 | 17 | @Override 18 | public void deposit(double amount) { 19 | balance += amount; 20 | System.out.println("Deposited: " + amount + " in Savings Account. New Balance: " + balance); 21 | } 22 | 23 | @Override 24 | public void withdraw(double amount) { 25 | if (balance >= amount) { 26 | balance -= amount; 27 | System.out.println("Withdrawn: " + amount + " from Savings Account. New Balance: " + balance); 28 | } else { 29 | System.out.println("Insufficient funds in Savings Account!"); 30 | } 31 | } 32 | } 33 | 34 | class CurrentAccount implements Account { 35 | private double balance; 36 | 37 | public CurrentAccount() { 38 | balance = 0; 39 | } 40 | 41 | @Override 42 | public void deposit(double amount) { 43 | balance += amount; 44 | System.out.println("Deposited: " + amount + " in Current Account. New Balance: " + balance); 45 | } 46 | 47 | @Override 48 | public void withdraw(double amount) { 49 | if (balance >= amount) { 50 | balance -= amount; 51 | System.out.println("Withdrawn: " + amount + " from Current Account. New Balance: " + balance); 52 | } else { 53 | System.out.println("Insufficient funds in Current Account!"); 54 | } 55 | } 56 | } 57 | 58 | class FixedTermAccount implements Account { 59 | private double balance; 60 | 61 | public FixedTermAccount() { 62 | balance = 0; 63 | } 64 | 65 | @Override 66 | public void deposit(double amount) { 67 | balance += amount; 68 | System.out.println("Deposited: " + amount + " in Fixed Term Account. New Balance: " + balance); 69 | } 70 | 71 | @Override 72 | public void withdraw(double amount) { 73 | throw new UnsupportedOperationException("Withdrawal not allowed in Fixed Term Account!"); 74 | } 75 | } 76 | 77 | class BankClient { 78 | private List accounts; 79 | 80 | public BankClient(List accounts) { 81 | this.accounts = accounts; 82 | } 83 | 84 | public void processTransactions() { 85 | for (Account acc : accounts) { 86 | acc.deposit(1000); // All accounts allow deposits 87 | 88 | // Assuming all accounts support withdrawal (LSP Violation) 89 | try { 90 | acc.withdraw(500); 91 | } catch (UnsupportedOperationException e) { 92 | System.out.println("Exception: " + e.getMessage()); 93 | } 94 | } 95 | } 96 | } 97 | 98 | public class LSPViolated { 99 | public static void main(String[] args) { 100 | List accounts = new ArrayList<>(); 101 | accounts.add(new SavingAccount()); 102 | accounts.add(new CurrentAccount()); 103 | accounts.add(new FixedTermAccount()); 104 | 105 | BankClient client = new BankClient(accounts); 106 | client.processTransactions(); // Throws exception when withdrawing from FixedTermAccount 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Lecture 05/Java Code/OCP/OCPFollowed.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | // Product class representing any item in eCommerce. 5 | class Product { 6 | public String name; 7 | public double price; 8 | 9 | public Product(String name, double price) { 10 | this.name = name; 11 | this.price = price; 12 | } 13 | } 14 | 15 | // 1. ShoppingCart: Only responsible for Cart related business logic. 16 | class ShoppingCart { 17 | private List products = new ArrayList<>(); 18 | 19 | public void addProduct(Product p) { 20 | products.add(p); 21 | } 22 | 23 | public List getProducts() { 24 | return products; 25 | } 26 | 27 | // Calculates total price in cart. 28 | public double calculateTotal() { 29 | double total = 0; 30 | for (Product p : products) { 31 | total += p.price; 32 | } 33 | return total; 34 | } 35 | } 36 | 37 | // 2. ShoppingCartPrinter: Only responsible for printing invoices 38 | class ShoppingCartPrinter { 39 | private ShoppingCart cart; 40 | 41 | public ShoppingCartPrinter(ShoppingCart cart) { 42 | this.cart = cart; 43 | } 44 | 45 | public void printInvoice() { 46 | System.out.println("Shopping Cart Invoice:"); 47 | for (Product p : cart.getProducts()) { 48 | System.out.println(p.name + " - Rs " + p.price); 49 | } 50 | System.out.println("Total: Rs " + cart.calculateTotal()); 51 | } 52 | } 53 | 54 | interface Persistence { 55 | void save(ShoppingCart cart); 56 | } 57 | 58 | class SQLPersistence implements Persistence { 59 | @Override 60 | public void save(ShoppingCart cart) { 61 | System.out.println("Saving shopping cart to SQL DB..."); 62 | } 63 | } 64 | 65 | class MongoPersistence implements Persistence { 66 | @Override 67 | public void save(ShoppingCart cart) { 68 | System.out.println("Saving shopping cart to MongoDB..."); 69 | } 70 | } 71 | 72 | class FilePersistence implements Persistence { 73 | @Override 74 | public void save(ShoppingCart cart) { 75 | System.out.println("Saving shopping cart to a file..."); 76 | } 77 | } 78 | 79 | public class OCPFollowed { 80 | public static void main(String[] args) { 81 | ShoppingCart cart = new ShoppingCart(); 82 | cart.addProduct(new Product("Laptop", 50000)); 83 | cart.addProduct(new Product("Mouse", 2000)); 84 | 85 | ShoppingCartPrinter printer = new ShoppingCartPrinter(cart); 86 | printer.printInvoice(); 87 | 88 | Persistence db = new SQLPersistence(); 89 | Persistence mongo = new MongoPersistence(); 90 | Persistence file = new FilePersistence(); 91 | 92 | db.save(cart); // Save to SQL database 93 | mongo.save(cart); // Save to MongoDB 94 | file.save(cart); // Save to File 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Lecture 05/Java Code/OCP/OCPViolated.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | // Product class representing any item in eCommerce. 5 | class Product { 6 | String name; 7 | double price; 8 | 9 | Product(String name, double price) { 10 | this.name = name; 11 | this.price = price; 12 | } 13 | } 14 | 15 | // 1. ShoppingCart: Only responsible for Cart related business logic. 16 | class ShoppingCart { 17 | private List products = new ArrayList<>(); 18 | 19 | void addProduct(Product p) { 20 | products.add(p); 21 | } 22 | 23 | List getProducts() { 24 | return products; 25 | } 26 | 27 | double calculateTotal() { 28 | double total = 0; 29 | for (Product p : products) { 30 | total += p.price; 31 | } 32 | return total; 33 | } 34 | } 35 | 36 | // 2. ShoppingCartPrinter: Only responsible for printing invoices 37 | class ShoppingCartPrinter { 38 | private ShoppingCart cart; 39 | 40 | ShoppingCartPrinter(ShoppingCart cart) { 41 | this.cart = cart; 42 | } 43 | 44 | void printInvoice() { 45 | System.out.println("Shopping Cart Invoice:"); 46 | for (Product p : cart.getProducts()) { 47 | System.out.println(p.name + " - Rs " + p.price); 48 | } 49 | System.out.println("Total: Rs " + cart.calculateTotal()); 50 | } 51 | } 52 | 53 | // 3. ShoppingCartStorage: Only responsible for saving cart to DB 54 | class ShoppingCartStorage { 55 | private ShoppingCart cart; 56 | 57 | ShoppingCartStorage(ShoppingCart cart) { 58 | this.cart = cart; 59 | } 60 | 61 | void saveToSQLDatabase() { 62 | System.out.println("Saving shopping cart to SQL DB..."); 63 | } 64 | 65 | void saveToMongoDatabase() { 66 | System.out.println("Saving shopping cart to Mongo DB..."); 67 | } 68 | 69 | void saveToFile() { 70 | System.out.println("Saving shopping cart to File..."); 71 | } 72 | } 73 | 74 | public class OCPViolated { 75 | public static void main(String[] args) { 76 | ShoppingCart cart = new ShoppingCart(); 77 | 78 | cart.addProduct(new Product("Laptop", 50000)); 79 | cart.addProduct(new Product("Mouse", 2000)); 80 | 81 | ShoppingCartPrinter printer = new ShoppingCartPrinter(cart); 82 | printer.printInvoice(); 83 | 84 | ShoppingCartStorage db = new ShoppingCartStorage(cart); 85 | db.saveToSQLDatabase(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Lecture 05/Java Code/SRP/SRPFollowed.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | // Product class representing any item in eCommerce. 5 | class Product { 6 | public String name; 7 | public double price; 8 | 9 | public Product(String name, double price) { 10 | this.name = name; 11 | this.price = price; 12 | } 13 | } 14 | 15 | // 1. ShoppingCart: Only responsible for Cart related business logic. 16 | class ShoppingCart { 17 | private List products = new ArrayList<>(); 18 | 19 | public void addProduct(Product p) { 20 | products.add(p); 21 | } 22 | 23 | public List getProducts() { 24 | return products; 25 | } 26 | 27 | // Calculates total price in cart. 28 | public double calculateTotal() { 29 | double total = 0; 30 | for (Product p : products) { 31 | total += p.price; 32 | } 33 | return total; 34 | } 35 | } 36 | 37 | // 2. ShoppingCartPrinter: Only responsible for printing invoices 38 | class ShoppingCartPrinter { 39 | private ShoppingCart cart; 40 | 41 | public ShoppingCartPrinter(ShoppingCart cart) { 42 | this.cart = cart; 43 | } 44 | 45 | public void printInvoice() { 46 | System.out.println("Shopping Cart Invoice:"); 47 | for (Product p : cart.getProducts()) { 48 | System.out.println(p.name + " - Rs " + p.price); 49 | } 50 | System.out.println("Total: Rs " + cart.calculateTotal()); 51 | } 52 | } 53 | 54 | // 3. ShoppingCartStorage: Only responsible for saving cart to DB 55 | class ShoppingCartStorage { 56 | private ShoppingCart cart; 57 | 58 | public ShoppingCartStorage(ShoppingCart cart) { 59 | this.cart = cart; 60 | } 61 | 62 | public void saveToDatabase() { 63 | System.out.println("Saving shopping cart to database..."); 64 | } 65 | } 66 | 67 | public class SRPFollowed { 68 | public static void main(String[] args) { 69 | ShoppingCart cart = new ShoppingCart(); 70 | 71 | cart.addProduct(new Product("Laptop", 50000)); 72 | cart.addProduct(new Product("Mouse", 2000)); 73 | 74 | ShoppingCartPrinter printer = new ShoppingCartPrinter(cart); 75 | printer.printInvoice(); 76 | 77 | ShoppingCartStorage db = new ShoppingCartStorage(cart); 78 | db.saveToDatabase(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Lecture 05/Java Code/SRP/SRPViolated.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | // Product class representing any item of any ECommerce. 5 | class Product { 6 | public String name; 7 | public double price; 8 | 9 | public Product(String name, double price) { 10 | this.name = name; 11 | this.price = price; 12 | } 13 | } 14 | 15 | // Violating SRP: ShoppingCart is handling multiple responsibilities 16 | class ShoppingCart { 17 | private List products = new ArrayList<>(); 18 | 19 | public void addProduct(Product p) { 20 | products.add(p); 21 | } 22 | 23 | public List getProducts() { 24 | return products; 25 | } 26 | 27 | // 1. Calculates total price in cart. 28 | public double calculateTotal() { 29 | double total = 0; 30 | for (Product p : products) { 31 | total += p.price; 32 | } 33 | return total; 34 | } 35 | 36 | // 2. Violating SRP - Prints invoice (Should be in a separate class) 37 | public void printInvoice() { 38 | System.out.println("Shopping Cart Invoice:"); 39 | for (Product p : products) { 40 | System.out.println(p.name + " - Rs " + p.price); 41 | } 42 | System.out.println("Total: Rs " + calculateTotal()); 43 | } 44 | 45 | // 3. Violating SRP - Saves to DB (Should be in a separate class) 46 | public void saveToDatabase() { 47 | System.out.println("Saving shopping cart to database..."); 48 | } 49 | } 50 | 51 | public class SRPViolated { 52 | public static void main(String[] args) { 53 | ShoppingCart cart = new ShoppingCart(); 54 | 55 | cart.addProduct(new Product("Laptop", 50000)); 56 | cart.addProduct(new Product("Mouse", 2000)); 57 | 58 | cart.printInvoice(); 59 | cart.saveToDatabase(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/DIP/DIP_followed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // Abstraction (Interface) 5 | class Database { 6 | public: 7 | virtual void save(string data) = 0; // Pure virtual function 8 | }; 9 | 10 | // MySQL implementation (Low-level module) 11 | class MySQLDatabase : public Database { 12 | public: 13 | void save(string data) override { 14 | cout << "Executing SQL Query: INSERT INTO users VALUES('" << data << "');" << endl; 15 | } 16 | }; 17 | 18 | // MongoDB implementation (Low-level module) 19 | class MongoDBDatabase : public Database { 20 | public: 21 | void save(string data) override { 22 | cout << "Executing MongoDB Function: db.users.insert({name: '" << data << "'})" << endl; 23 | } 24 | }; 25 | 26 | // High-level module (Now loosely coupled) 27 | class UserService { 28 | private: 29 | Database* db; // Dependency Injection 30 | 31 | public: 32 | UserService(Database* database) { 33 | db = database; 34 | } 35 | 36 | void storeUser(string user) { 37 | db->save(user); 38 | } 39 | }; 40 | 41 | int main() { 42 | MySQLDatabase mysql; 43 | MongoDBDatabase mongodb; 44 | 45 | UserService service1(&mysql); 46 | service1.storeUser("Aditya"); 47 | 48 | UserService service2(&mongodb); 49 | service2.storeUser("Rohit"); 50 | } 51 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/DIP/DIP_violated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class MySQLDatabase { // Low-level module 5 | public: 6 | void saveToSQL(string data) { 7 | cout << "Executing SQL Query: INSERT INTO users VALUES('" << data << "');" << endl; 8 | } 9 | }; 10 | 11 | class MongoDBDatabase { // Low-level module 12 | public: 13 | void saveToMongo(string data) { 14 | cout << "Executing MongoDB Function: db.users.insert({name: '" << data << "'})" << endl; 15 | } 16 | }; 17 | 18 | class UserService { // High-level module (Tightly coupled) 19 | private: 20 | MySQLDatabase sqlDb; // Direct dependency on MySQL 21 | MongoDBDatabase mongoDb; // Direct dependency on MongoDB 22 | 23 | public: 24 | void storeUserToSQL(string user) { 25 | // MySQL-specific code 26 | sqlDb.saveToSQL(user); 27 | } 28 | 29 | void storeUserToMongo(string user) { 30 | // MongoDB-specific code 31 | mongoDb.saveToMongo(user); 32 | } 33 | }; 34 | 35 | int main() { 36 | UserService service; 37 | service.storeUserToSQL("Aditya"); 38 | service.storeUserToMongo("Rohit"); 39 | } 40 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/ISP/ISP_followed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Separate interface for 2D shapes 6 | class TwoDimensionalShape { 7 | public: 8 | virtual double area() = 0; 9 | }; 10 | 11 | // Separate interface for 3D shapes 12 | class ThreeDimensionalShape { 13 | public: 14 | virtual double area() = 0; 15 | virtual double volume() = 0; 16 | }; 17 | 18 | // Square implements only the 2D interface 19 | class Square : public TwoDimensionalShape { 20 | private: 21 | double side; 22 | 23 | public: 24 | Square(double s) : side(s) {} 25 | 26 | double area() override { 27 | return side * side; 28 | } 29 | }; 30 | 31 | // Rectangle implements only the 2D interface 32 | class Rectangle : public TwoDimensionalShape { 33 | private: 34 | double length, width; 35 | 36 | public: 37 | Rectangle(double l, double w) : length(l), width(w) {} 38 | 39 | double area() override { 40 | return length * width; 41 | } 42 | }; 43 | 44 | // Cube implements the 3D interface 45 | class Cube : public ThreeDimensionalShape { 46 | private: 47 | double side; 48 | 49 | public: 50 | Cube(double s) : side(s) {} 51 | 52 | double area() override { 53 | return 6 * side * side; 54 | } 55 | 56 | double volume() override { 57 | return side * side * side; 58 | } 59 | }; 60 | 61 | int main() { 62 | TwoDimensionalShape* square = new Square(5); 63 | TwoDimensionalShape* rectangle = new Rectangle(4, 6); 64 | ThreeDimensionalShape* cube = new Cube(3); 65 | 66 | cout << "Square Area: " << square->area() << endl; 67 | cout << "Rectangle Area: " << rectangle->area() << endl; 68 | cout << "Cube Area: " << cube->area() << endl; 69 | cout << "Cube Volume: " << cube->volume() << endl; 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/ISP/ISP_violated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Single interface for all shapes (Violates ISP) 7 | class Shape { 8 | public: 9 | virtual double area() = 0; 10 | virtual double volume() = 0; // 2D shapes don't have volume! 11 | }; 12 | 13 | // Square is a 2D shape but is forced to implement volume() 14 | class Square : public Shape { 15 | private: 16 | double side; 17 | 18 | public: 19 | Square(double s) : side(s) {} 20 | 21 | double area() override { 22 | return side * side; 23 | } 24 | 25 | double volume() override { 26 | throw logic_error("Volume not applicable for Square"); // Unnecessary method 27 | } 28 | }; 29 | 30 | // Rectangle is also a 2D shape but is forced to implement volume() 31 | class Rectangle : public Shape { 32 | private: 33 | double length, width; 34 | 35 | public: 36 | Rectangle(double l, double w) : length(l), width(w) {} 37 | 38 | double area() override { 39 | return length * width; 40 | } 41 | 42 | double volume() override { 43 | throw logic_error("Volume not applicable for Rectangle"); // Unnecessary method 44 | } 45 | }; 46 | 47 | // Cube is a 3D shape, so it actually has a volume 48 | class Cube : public Shape { 49 | private: 50 | double side; 51 | 52 | public: 53 | Cube(double s) : side(s) {} 54 | 55 | double area() override { 56 | return 6 * side * side; 57 | } 58 | 59 | double volume() override { 60 | return side * side * side; 61 | } 62 | }; 63 | 64 | int main() { 65 | Shape* square = new Square(5); 66 | Shape* rectangle = new Rectangle(4, 6); 67 | Shape* cube = new Cube(3); 68 | 69 | cout << "Square Area: " << square->area() << endl; 70 | cout << "Rectangle Area: " << rectangle->area() << endl; 71 | cout << "Cube Area: " << cube->area() << endl; 72 | cout << "Cube Volume: " << cube->volume() << endl; 73 | 74 | try { 75 | cout << "Square Volume: " << square->volume() << endl; // Will throw an exception 76 | } catch (logic_error& e) { 77 | cout << "Exception: " << e.what() << endl; 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/MethodRules/PostConditions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // A Postcondition must be statisfied after a method is executed. 6 | // Sub classes can strengthen the Postcondition but cannot weaken it. 7 | 8 | class Car { 9 | protected: 10 | int speed; 11 | 12 | public: 13 | Car() { 14 | speed = 0; 15 | } 16 | 17 | void accelerate() { 18 | cout << "Accelerating" << endl; 19 | speed += 20; 20 | } 21 | 22 | //PostCondition : Speed must reduce after brake 23 | virtual void brake() { 24 | cout << "Applying brakes" << endl; 25 | speed -= 20; 26 | } 27 | }; 28 | 29 | // Subclass can strengthen postcondition - Does not violate LSP 30 | class HybridCar : public Car { 31 | private: 32 | int charge; 33 | 34 | public: 35 | 36 | HybridCar() : Car() { 37 | charge = 0; 38 | } 39 | 40 | // PostCondition : Speed must reduce after brake 41 | // PostCondition : Charge must increase. 42 | void brake() { 43 | cout << "Applying brakes" << endl; 44 | speed -= 20; 45 | charge += 10; 46 | } 47 | }; 48 | 49 | 50 | int main() { 51 | Car* hybridCar = new HybridCar(); 52 | hybridCar->brake(); // Works fine: HybridCar reduces speed and also increases charge. 53 | 54 | //Client feels no difference in substituting Hybrid car in place of Car. 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/MethodRules/PreConditions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // A Precondition must be statisfied before a method can be executed. 6 | // Sub classes can weaken the precondition but cannot strengthen it. 7 | 8 | class User { 9 | public: 10 | // Precondition: Password must be at least 8 characters long 11 | virtual void setPassword(string password) { 12 | if (password.length() < 8) { 13 | throw invalid_argument("Password must be at least 8 characters long!"); 14 | } 15 | cout << "Password set successfully" << endl; 16 | } 17 | }; 18 | 19 | class AdminUser : public User { 20 | public: 21 | // Precondition: Password must be at least 6 characters 22 | void setPassword(string password) override { 23 | if (password.length() < 6) { 24 | throw invalid_argument("Password must be at least 6 characters long!"); 25 | } 26 | cout << "Password set successfully" << endl; 27 | } 28 | }; 29 | 30 | int main() { 31 | User* user = new AdminUser(); 32 | user->setPassword("Admin1"); // Works fine: AdminUser allows shorter passwords 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/PropertiesRules/ClassInvariants.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Class Invariant of a parent class Object should not be broken by child class Object. 6 | // Hence child class can either maintain or strengthen the invariant but never narrows it down. 7 | 8 | //Invariant : Balance cannot be negative 9 | class BankAccount { 10 | protected: 11 | double balance; 12 | public: 13 | BankAccount(double b) { 14 | if (b < 0) throw invalid_argument("Balance can't be negative"); 15 | balance = b; 16 | } 17 | virtual void withdraw(double amount) { 18 | if (balance - amount < 0) throw runtime_error("Insufficient funds"); 19 | balance -= amount; 20 | cout<< "Amount withdrawn. Remaining balance is " << balance << endl; 21 | } 22 | }; 23 | 24 | //Brakes invariant : Should not be allowed. 25 | class CheatAccount : public BankAccount { 26 | public: 27 | CheatAccount(double b) : BankAccount(b) {} 28 | 29 | void withdraw(double amount) override { 30 | balance -= amount; // LSP break! Negative balance allowed 31 | cout<< "Amount withdrawn. Remaining balance is " << balance << endl; 32 | } 33 | }; 34 | 35 | int main() { 36 | BankAccount* bankAccount = new BankAccount(100); 37 | bankAccount->withdraw(100); 38 | } 39 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/PropertiesRules/HistoryConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Sub class methods should not be allowed state changes What 6 | // Base class never allowed. 7 | 8 | class BankAccount { 9 | protected: 10 | double balance; 11 | 12 | public: 13 | BankAccount(double b) { 14 | if (b < 0) throw invalid_argument("Balance can't be negative"); 15 | balance = b; 16 | } 17 | 18 | // History Constraint : Withdraw should be allowed 19 | virtual void withdraw(double amount) { 20 | if (balance - amount < 0) throw runtime_error("Insufficient funds"); 21 | balance -= amount; 22 | cout<< "Amount withdrawn. Remaining balance is " << balance << endl; 23 | } 24 | }; 25 | 26 | 27 | class FixedDepositAccount : public BankAccount { 28 | public: 29 | FixedDepositAccount(double b) : BankAccount(b) {} 30 | 31 | // LSP break! History constraint broke! 32 | // Parent class behaviour change : Now withdraw is not allowed. 33 | //This class will brake client code that relies on withdraw. 34 | void withdraw(double amount) override { 35 | throw runtime_error("Withdraw not allowed in Fixed Deposit"); 36 | } 37 | }; 38 | 39 | int main() { 40 | BankAccount* bankAccount = new BankAccount(100); 41 | bankAccount->withdraw(100); 42 | } -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/SingatureRules/ExceptionRule.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Exception Rule: 6 | // A subclass should throw fewer or narrower exceptions 7 | // (but not additional or broader exceptions) than the parent. 8 | // C++ does not enforces this. Hence no compilation error. 9 | 10 | /* 11 | ├── std::logic_error <-- For logical errors detected before runtime 12 | │ ├── std::invalid_argument <-- Invalid function argument 13 | │ ├── std::domain_error <-- Function argument domain error 14 | │ ├── std::length_error <-- Exceeding valid length limits 15 | │ ├── std::out_of_range <-- Array or container index out of bounds 16 | │ 17 | ├── std::runtime_error <-- For errors that occur at runtime 18 | │ ├── std::range_error <-- Numeric result out of range 19 | │ ├── std::overflow_error <-- Arithmetic overflow 20 | │ ├── std::underflow_error 21 | */ 22 | 23 | class Parent { 24 | public: 25 | virtual void getValue() noexcept(false) { // Parent throws logic_error exception 26 | throw logic_error("Parent error"); 27 | } 28 | }; 29 | 30 | class Child : public Parent { 31 | public: 32 | void getValue() noexcept(false) override { // Child throws out_of_range exception 33 | throw out_of_range("Child error"); 34 | // throw runtime_error("Child Error"); // This is Wrong 35 | } 36 | }; 37 | 38 | class Client { 39 | private: 40 | Parent* p; 41 | 42 | public: 43 | Client(Parent* p) { 44 | this->p = p; 45 | } 46 | void takeValue() { 47 | try { 48 | p->getValue(); 49 | } 50 | catch(const logic_error& e) { 51 | cout << "Logic error exception occured : " << e.what() << endl; 52 | } 53 | } 54 | }; 55 | 56 | int main() { 57 | Parent* parent = new Parent(); 58 | Child* child = new Child(); 59 | 60 | Client* client = new Client(parent); 61 | //Client* client = new Client(child); 62 | 63 | client->takeValue(); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/SingatureRules/MethodArgumentRule.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Method Argument Rule : 6 | // Subtype method arguments can be identical or wider than the supertype 7 | // C++ imposes this by keeping singature identical 8 | 9 | class Parent { 10 | public: 11 | virtual void print(string msg) { 12 | cout << "Parent: " << msg << endl; 13 | } 14 | }; 15 | 16 | class Child : public Parent { 17 | public: 18 | void print(string msg) override { 19 | cout << "Child: " << msg << endl; 20 | } 21 | }; 22 | 23 | //Client that pass string as msg as client expects. 24 | class Client { 25 | private: 26 | Parent* p; 27 | 28 | public: 29 | Client(Parent* p) { 30 | this->p = p; 31 | } 32 | void printMsg() { 33 | p->print("Hello"); 34 | } 35 | }; 36 | 37 | int main() { 38 | 39 | Parent* parent = new Parent(); 40 | Parent* child = new Child(); 41 | 42 | //Client* client = new Client(parent); 43 | Client* client = new Client(child); 44 | 45 | client->printMsg(); 46 | 47 | 48 | return 0; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Lecture 06/C++ Code/LSP-Rules/SingatureRules/ReturnTypeRule.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Return Type Rule : 6 | // Subtype overriden method return type should be either identical 7 | // or narrower then the parent method's return type. 8 | // This is also called as return type covariance. 9 | // C++ enforces this by covariance. 10 | 11 | class Animal { 12 | //some common Animal methods 13 | }; 14 | 15 | class Dog : public Animal { 16 | //Additional Dog methods specific to Dogs. 17 | }; 18 | 19 | 20 | class Parent { 21 | public: 22 | virtual Animal* getAnimal() { 23 | cout << "Parent : Returning Animal instance" << endl; 24 | return new Animal(); 25 | } 26 | }; 27 | 28 | class Child : public Parent { 29 | public: 30 | // Can also have return type as Dog 31 | Animal* getAnimal() override { 32 | cout << "Child : Returning Dog instance" << std::endl; 33 | return new Dog(); 34 | } 35 | }; 36 | 37 | class Client { 38 | private: 39 | Parent* p; 40 | 41 | public: 42 | Client(Parent* p) { 43 | this->p = p; 44 | } 45 | void takeAnimal() { 46 | p->getAnimal(); 47 | } 48 | }; 49 | 50 | int main() { 51 | Parent* parent = new Parent(); 52 | Child* child = new Child(); 53 | 54 | Client* client = new Client(child); 55 | //Client * client = new Client(parent); 56 | client->takeAnimal(); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/DIP/DIPFollowed.java: -------------------------------------------------------------------------------- 1 | // Abstraction (Interface) 2 | interface Database { 3 | void save(String data); 4 | } 5 | 6 | // MySQL implementation (Low-level module) 7 | class MySQLDatabase implements Database { 8 | @Override 9 | public void save(String data) { 10 | System.out.println( 11 | "Executing SQL Query: INSERT INTO users VALUES('" 12 | + data + "');" 13 | ); 14 | } 15 | } 16 | 17 | // MongoDB implementation (Low-level module) 18 | class MongoDBDatabase implements Database { 19 | @Override 20 | public void save(String data) { 21 | System.out.println( 22 | "Executing MongoDB Function: db.users.insert({name: '" 23 | + data + "'})" 24 | ); 25 | } 26 | } 27 | 28 | // High-level module (Now loosely coupled via Dependency Injection) 29 | class UserService { 30 | private final Database db; 31 | 32 | public UserService(Database database) { 33 | this.db = database; 34 | } 35 | 36 | public void storeUser(String user) { 37 | db.save(user); 38 | } 39 | } 40 | 41 | public class DIPFollowed { 42 | public static void main(String[] args) { 43 | MySQLDatabase mysql = new MySQLDatabase(); 44 | MongoDBDatabase mongodb = new MongoDBDatabase(); 45 | 46 | UserService service1 = new UserService(mysql); 47 | service1.storeUser("Aditya"); 48 | 49 | UserService service2 = new UserService(mongodb); 50 | service2.storeUser("Rohit"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/DIP/DIPViolated.java: -------------------------------------------------------------------------------- 1 | class MySQLDatabase { // Low-level module 2 | public void saveToSQL(String data) { 3 | System.out.println( 4 | "Executing SQL Query: INSERT INTO users VALUES('" 5 | + data + "');" 6 | ); 7 | } 8 | } 9 | 10 | class MongoDBDatabase { // Low-level module 11 | public void saveToMongo(String data) { 12 | System.out.println( 13 | "Executing MongoDB Function: db.users.insert({name: '" 14 | + data + "'})" 15 | ); 16 | } 17 | } 18 | 19 | class UserService { // High-level module (Tightly coupled) 20 | private final MySQLDatabase sqlDb = new MySQLDatabase(); 21 | private final MongoDBDatabase mongoDb = new MongoDBDatabase(); 22 | 23 | public void storeUserToSQL(String user) { 24 | // MySQL-specific code 25 | sqlDb.saveToSQL(user); 26 | } 27 | 28 | public void storeUserToMongo(String user) { 29 | // MongoDB-specific code 30 | mongoDb.saveToMongo(user); 31 | } 32 | } 33 | 34 | public class DIPViolated { 35 | public static void main(String[] args) { 36 | UserService service = new UserService(); 37 | service.storeUserToSQL("Aditya"); 38 | service.storeUserToMongo("Rohit"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/ISP/ISPFollowed.java: -------------------------------------------------------------------------------- 1 | 2 | // Separate interface for 2D shapes 3 | interface TwoDimensionalShape { 4 | double area(); 5 | } 6 | 7 | // Separate interface for 3D shapes 8 | interface ThreeDimensionalShape { 9 | double area(); 10 | double volume(); 11 | } 12 | 13 | // Square implements only the 2D interface 14 | class Square implements TwoDimensionalShape { 15 | private double side; 16 | 17 | public Square(double s) { 18 | this.side = s; 19 | } 20 | 21 | @Override 22 | public double area() { 23 | return side * side; 24 | } 25 | } 26 | 27 | // Rectangle implements only the 2D interface 28 | class Rectangle implements TwoDimensionalShape { 29 | private double length, width; 30 | 31 | public Rectangle(double l, double w) { 32 | this.length = l; 33 | this.width = w; 34 | } 35 | 36 | @Override 37 | public double area() { 38 | return length * width; 39 | } 40 | } 41 | 42 | // Cube implements the 3D interface 43 | class Cube implements ThreeDimensionalShape { 44 | private double side; 45 | 46 | public Cube(double s) { 47 | this.side = s; 48 | } 49 | 50 | @Override 51 | public double area() { 52 | return 6 * side * side; 53 | } 54 | 55 | @Override 56 | public double volume() { 57 | return side * side * side; 58 | } 59 | } 60 | 61 | public class ISPFollowed { 62 | public static void main(String[] args) { 63 | TwoDimensionalShape square = new Square(5); 64 | TwoDimensionalShape rectangle = new Rectangle(4, 6); 65 | ThreeDimensionalShape cube = new Cube(3); 66 | 67 | System.out.println("Square Area: " + square.area()); 68 | System.out.println("Rectangle Area: " + rectangle.area()); 69 | System.out.println("Cube Area: " + cube.area()); 70 | System.out.println("Cube Volume: " + cube.volume()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/ISP/ISPViolated.java: -------------------------------------------------------------------------------- 1 | 2 | // Single interface for all shapes (Violates ISP) 3 | interface Shape { 4 | double area(); 5 | double volume(); // 2D shapes don't have volume! 6 | } 7 | 8 | // Square is a 2D shape but is forced to implement volume() 9 | class Square implements Shape { 10 | private double side; 11 | 12 | public Square(double s) { 13 | this.side = s; 14 | } 15 | 16 | @Override 17 | public double area() { 18 | return side * side; 19 | } 20 | 21 | @Override 22 | public double volume() { 23 | throw new UnsupportedOperationException("Volume not applicable for Square"); // Unnecessary method 24 | } 25 | } 26 | 27 | // Rectangle is also a 2D shape but is forced to implement volume() 28 | class Rectangle implements Shape { 29 | private double length, width; 30 | 31 | public Rectangle(double l, double w) { 32 | this.length = l; 33 | this.width = w; 34 | } 35 | 36 | @Override 37 | public double area() { 38 | return length * width; 39 | } 40 | 41 | @Override 42 | public double volume() { 43 | throw new UnsupportedOperationException("Volume not applicable for Rectangle"); // Unnecessary method 44 | } 45 | } 46 | 47 | // Cube is a 3D shape, so it actually has a volume 48 | class Cube implements Shape { 49 | private double side; 50 | 51 | public Cube(double s) { 52 | this.side = s; 53 | } 54 | 55 | @Override 56 | public double area() { 57 | return 6 * side * side; 58 | } 59 | 60 | @Override 61 | public double volume() { 62 | return side * side * side; 63 | } 64 | } 65 | 66 | public class ISPViolated { 67 | public static void main(String[] args) { 68 | Shape square = new Square(5); 69 | Shape rectangle = new Rectangle(4, 6); 70 | Shape cube = new Cube(3); 71 | 72 | System.out.println("Square Area: " + square.area()); 73 | System.out.println("Rectangle Area: " + rectangle.area()); 74 | System.out.println("Cube Area: " + cube.area()); 75 | System.out.println("Cube Volume: " + cube.volume()); 76 | 77 | try { 78 | System.out.println("Square Volume: " + square.volume()); // Will throw an exception 79 | } catch (UnsupportedOperationException e) { 80 | System.out.println("Exception: " + e.getMessage()); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/MethodRules/PostConditions.java: -------------------------------------------------------------------------------- 1 | 2 | // A Postcondition must be satisfied after a method is executed. 3 | // Subclasses can strengthen the Postcondition but cannot weaken it. 4 | 5 | class Car { 6 | protected int speed; 7 | 8 | public Car() { 9 | speed = 0; 10 | } 11 | 12 | public void accelerate() { 13 | System.out.println("Accelerating"); 14 | speed += 20; 15 | } 16 | 17 | // PostCondition: Speed must reduce after brake 18 | public void brake() { 19 | System.out.println("Applying brakes"); 20 | speed -= 20; 21 | } 22 | } 23 | 24 | // Subclass can strengthen postcondition - Does not violate LSP 25 | class HybridCar extends Car { 26 | private int charge; 27 | 28 | public HybridCar() { 29 | super(); 30 | charge = 0; 31 | } 32 | 33 | // PostCondition: Speed must reduce after brake 34 | // PostCondition: Charge must increase. 35 | @Override 36 | public void brake() { 37 | System.out.println("Applying brakes"); 38 | speed -= 20; 39 | charge += 10; 40 | } 41 | } 42 | 43 | public class PostConditions { 44 | public static void main(String[] args) { 45 | Car hybridCar = new HybridCar(); 46 | hybridCar.brake(); // Works fine: HybridCar reduces speed and also increases charge. 47 | 48 | //Client feels no difference in substituting Hybrid car in place of Car. 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/MethodRules/PreConditions.java: -------------------------------------------------------------------------------- 1 | 2 | // A Precondition must be statisfied before a method can be executed. 3 | // Sub classes can weaken the precondition but cannot strengthen it. 4 | 5 | class User { 6 | // Precondition: Password must be at least 8 characters long 7 | public void setPassword(String password) { 8 | if (password.length() < 8) { 9 | throw new IllegalArgumentException("Password must be at least 8 characters long!"); 10 | } 11 | System.out.println("Password set successfully"); 12 | } 13 | } 14 | 15 | class AdminUser extends User { 16 | // Precondition: Password must be at least 6 characters 17 | @Override 18 | public void setPassword(String password) { 19 | if (password.length() < 6) { 20 | throw new IllegalArgumentException("Password must be at least 6 characters long!"); 21 | } 22 | System.out.println("Password set successfully"); 23 | } 24 | } 25 | 26 | public class PreConditions { 27 | public static void main(String[] args) { 28 | User user = new AdminUser(); 29 | user.setPassword("Admin1"); // Works fine: AdminUser allows shorter passwords 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/PropertiesRules/ClassInvariants.java: -------------------------------------------------------------------------------- 1 | // Class Invariant of a parent class Object should not be broken by child class Object. 2 | // Hence child class can either maintain or strengthen the invariant but never narrows it down. 3 | 4 | // Invariant: Balance cannot be negative 5 | class BankAccount { 6 | protected double balance; 7 | 8 | public BankAccount(double b) { 9 | if (b < 0) throw new IllegalArgumentException("Balance can't be negative"); 10 | balance = b; 11 | } 12 | 13 | public void withdraw(double amount) { 14 | if (balance - amount < 0) throw new RuntimeException("Insufficient funds"); 15 | balance -= amount; 16 | System.out.println("Amount withdrawn. Remaining balance is " + balance); 17 | } 18 | } 19 | 20 | // Breaks invariant: Should not be allowed. 21 | class CheatAccount extends BankAccount { 22 | public CheatAccount(double b) { 23 | super(b); 24 | } 25 | 26 | @Override 27 | public void withdraw(double amount) { 28 | balance -= amount; // LSP break! Negative balance allowed 29 | System.out.println("Amount withdrawn. Remaining balance is " + balance); 30 | } 31 | } 32 | 33 | public class ClassInvariants { 34 | public static void main(String[] args) { 35 | BankAccount bankAccount = new BankAccount(100); 36 | bankAccount.withdraw(100); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/PropertiesRules/HistoryConstraint.java: -------------------------------------------------------------------------------- 1 | // Subclass methods should not be allowed state changes that 2 | // the base class never allowed. 3 | 4 | class BankAccount { 5 | protected double balance; 6 | 7 | public BankAccount(double b) { 8 | if (b < 0) throw new IllegalArgumentException("Balance can't be negative"); 9 | this.balance = b; 10 | } 11 | 12 | // History Constraint: withdraw should be allowed 13 | public void withdraw(double amount) { 14 | if (balance - amount < 0) throw new RuntimeException("Insufficient funds"); 15 | balance -= amount; 16 | System.out.println("Amount withdrawn. Remaining balance is " + balance); 17 | } 18 | } 19 | 20 | class FixedDepositAccount extends BankAccount { 21 | public FixedDepositAccount(double b) { 22 | super(b); 23 | } 24 | 25 | // LSP break! History constraint broken! 26 | // Parent class behavior changed: Now withdraw is not allowed. 27 | // This class will break client code that relies on withdraw. 28 | @Override 29 | public void withdraw(double amount) { 30 | throw new RuntimeException("Withdraw not allowed in Fixed Deposit"); 31 | } 32 | } 33 | 34 | public class HistoryConstraint { 35 | public static void main(String[] args) { 36 | BankAccount bankAccount = new BankAccount(100); 37 | bankAccount.withdraw(100); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/SingatureRules/ExceptionRule.java: -------------------------------------------------------------------------------- 1 | 2 | // Exception Rule: 3 | // A subclass should throw fewer or narrower exceptions 4 | // (but not additional or broader exceptions) than the parent. 5 | // Java enforces this only for checked Exceptions. 6 | 7 | /* 8 | └── java.lang.Exception // Conditions your application might want to catch 9 | ├── java.io.IOException // Checked I/O failures 10 | │ ├── java.io.FileNotFoundException 11 | │ ├── java.io.EOFException 12 | │ └── java.net.MalformedURLException 13 | ├── java.lang.ClassNotFoundException // Checked reflect/… failures 14 | ├── java.lang.InterruptedException // Checked thread interruption 15 | ├── java.sql.SQLException // Checked SQL/database errors 16 | ├── java.text.ParseException // Checked parsing errors 17 | └── java.lang.RuntimeException // Unchecked; subclasses may be thrown anywhere 18 | ├── java.lang.ArithmeticException // e.g. divide by zero 19 | ├── java.lang.NullPointerException 20 | ├── java.lang.ArrayIndexOutOfBoundsException 21 | ├── java.lang.StringIndexOutOfBoundsException 22 | ├── java.lang.IllegalArgumentException 23 | │ └── java.lang.NumberFormatException 24 | ├── java.lang.IllegalStateException 25 | ├── java.lang.UnsupportedOperationException 26 | └── java.lang.IndexOutOfBoundsException // parent of the two “…OutOfBounds” above 27 | */ 28 | 29 | class Parent { 30 | public void getValue() throws RuntimeException { 31 | throw new RuntimeException("Parent error"); 32 | } 33 | } 34 | 35 | // Subclass overrides getValue and throws the narrower ChildException 36 | class Child extends Parent { 37 | @Override 38 | public void getValue() throws ArithmeticException { 39 | throw new ArithmeticException("Child error"); 40 | //throw new Exception("Child error"); // This is wrong & not allowed 41 | } 42 | } 43 | 44 | // Client that invokes getValue and catches the parent exception type 45 | class Client { 46 | private Parent p; 47 | 48 | public Client(Parent p) { 49 | this.p = p; 50 | } 51 | 52 | public void takeValue() { 53 | try { 54 | p.getValue(); 55 | } catch (RuntimeException e) { 56 | System.out.println("RuntimeException occurred: " + e.getMessage()); 57 | } 58 | } 59 | } 60 | 61 | public class ExceptionRule { 62 | public static void main(String[] args) { 63 | Parent parent = new Parent(); 64 | Child child = new Child(); 65 | 66 | Client client = new Client(parent); 67 | //Client client = new Client(child); 68 | 69 | client.takeValue(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/SingatureRules/MethodArgumentRule.java: -------------------------------------------------------------------------------- 1 | // Method Argument Rule : 2 | // Subtype method arguments can be identical or wider than the supertype 3 | // Java enforces this by requiring the same method signature for overrides 4 | 5 | class Parent { 6 | public void print(String msg) { 7 | System.out.println("Parent: " + msg); 8 | } 9 | } 10 | 11 | class Child extends Parent { 12 | @Override 13 | public void print(String msg) { 14 | System.out.println("Child: " + msg); 15 | } 16 | } 17 | 18 | // Client that passes a String msg as the client expects. 19 | class Client { 20 | private Parent p; 21 | 22 | public Client(Parent p) { 23 | this.p = p; 24 | } 25 | 26 | public void printMsg() { 27 | p.print("Hello"); 28 | } 29 | } 30 | 31 | public class MethodArgumentRule { 32 | public static void main(String[] args) { 33 | Parent parent = new Parent(); 34 | Parent child = new Child(); 35 | 36 | Client client = new Client(parent); 37 | //Client client = new Client(child); 38 | 39 | client.printMsg(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Lecture 06/Java Code/LSP-Rules/SingatureRules/ReturnTypeRule.java: -------------------------------------------------------------------------------- 1 | // Return Type Rule : 2 | // Subtype overridden method return type should be either identical 3 | // or narrower than the parent method's return type. 4 | // This is also called return type covariance. 5 | // Java supports this out of the box. 6 | 7 | class Animal { 8 | // some common Animal methods 9 | } 10 | 11 | class Dog extends Animal { 12 | // Additional Dog methods specific to Dogs. 13 | } 14 | 15 | class Parent { 16 | public Animal getAnimal() { 17 | System.out.println("Parent : Returning Animal instance"); 18 | return new Animal(); 19 | } 20 | } 21 | 22 | class Child extends Parent { 23 | @Override 24 | public Animal getAnimal() { 25 | System.out.println("Child : Returning Dog instance"); 26 | return new Dog(); 27 | } 28 | } 29 | 30 | class Client { 31 | private Parent p; 32 | 33 | public Client(Parent p) { 34 | this.p = p; 35 | } 36 | 37 | public void takeAnimal() { 38 | p.getAnimal(); 39 | } 40 | } 41 | 42 | public class ReturnTypeRule { 43 | public static void main(String[] args) { 44 | Parent parent = new Parent(); 45 | Child child = new Child(); 46 | 47 | Client client = new Client(child); 48 | //Client client = new Client(parent); 49 | client.takeAnimal(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Lecture 06/notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 06/notes.pdf -------------------------------------------------------------------------------- /Lecture 07/C++ Code/BadDesign/DocumentEditor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | class DocumentEditor { 9 | private: 10 | vector documentElements; 11 | string renderedDocument; 12 | 13 | public: 14 | // Adds text as a plain string 15 | void addText(string text) { 16 | documentElements.push_back(text); 17 | } 18 | 19 | // Adds an image represented by its file path 20 | void addImage(string imagePath) { 21 | documentElements.push_back(imagePath); 22 | } 23 | 24 | // Renders the document by checking the type of each element at runtime 25 | string renderDocument() { 26 | if(renderedDocument.empty()) { 27 | string result; 28 | for (auto element : documentElements) { 29 | if (element.size() > 4 && (element.substr(element.size() - 4) == ".jpg" || 30 | element.substr(element.size() - 4) == ".png")) { 31 | result += "[Image: " + element + "]" + "\n"; 32 | } else { 33 | result += element + "\n"; 34 | } 35 | } 36 | renderedDocument = result; 37 | } 38 | return renderedDocument; 39 | } 40 | 41 | void saveToFile() { 42 | ofstream file("document.txt"); 43 | if (file.is_open()) { 44 | file << renderDocument(); 45 | file.close(); 46 | cout << "Document saved to document.txt" << endl; 47 | } else { 48 | cout << "Error: Unable to open file for writing." << endl; 49 | } 50 | } 51 | }; 52 | 53 | int main() { 54 | DocumentEditor editor; 55 | editor.addText("Hello, world!"); 56 | editor.addImage("picture.jpg"); 57 | editor.addText("This is a document editor."); 58 | 59 | cout << editor.renderDocument() << endl; 60 | 61 | editor.saveToFile(); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /Lecture 07/Java Code/Bad Design/DocumentEditorClient.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | 6 | class DocumentEditor { 7 | private List documentElements; 8 | private String renderedDocument; 9 | 10 | public DocumentEditor() { 11 | documentElements = new ArrayList<>(); 12 | renderedDocument = ""; 13 | } 14 | 15 | // Adds text as a plain string 16 | public void addText(String text) { 17 | documentElements.add(text); 18 | } 19 | 20 | // Adds an image represented by its file path 21 | public void addImage(String imagePath) { 22 | documentElements.add(imagePath); 23 | } 24 | 25 | // Renders the document by checking the type of each element at runtime 26 | public String renderDocument() { 27 | if (renderedDocument.isEmpty()) { 28 | StringBuilder result = new StringBuilder(); 29 | for (String element : documentElements) { 30 | if (element.length() > 4 && 31 | (element.endsWith(".jpg") || element.endsWith(".png"))) { 32 | result.append("[Image: ").append(element).append("]\n"); 33 | } else { 34 | result.append(element).append("\n"); 35 | } 36 | } 37 | renderedDocument = result.toString(); 38 | } 39 | return renderedDocument; 40 | } 41 | 42 | public void saveToFile() { 43 | try { 44 | FileWriter writer = new FileWriter("document.txt"); 45 | writer.write(renderDocument()); 46 | writer.close(); 47 | System.out.println("Document saved to document.txt"); 48 | } catch (IOException e) { 49 | System.out.println("Error: Unable to open file for writing."); 50 | } 51 | } 52 | } 53 | 54 | public class DocumentEditorClient { 55 | public static void main(String[] args) { 56 | DocumentEditor editor = new DocumentEditor(); 57 | editor.addText("Hello, world!"); 58 | editor.addImage("picture.jpg"); 59 | editor.addText("This is a document editor."); 60 | 61 | System.out.println(editor.renderDocument()); 62 | 63 | editor.saveToFile(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Lecture 07/standardUml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 07/standardUml.png -------------------------------------------------------------------------------- /Lecture 08/Java Code/StrategyDesignPattern.java: -------------------------------------------------------------------------------- 1 | // --- Strategy Interface for Walk --- 2 | interface WalkableRobot { 3 | void walk(); 4 | } 5 | 6 | // --- Concrete Strategies for walk --- 7 | class NormalWalk implements WalkableRobot { 8 | public void walk() { 9 | System.out.println("Walking normally..."); 10 | } 11 | } 12 | 13 | class NoWalk implements WalkableRobot { 14 | public void walk() { 15 | System.out.println("Cannot walk."); 16 | } 17 | } 18 | 19 | // --- Strategy Interface for Talk --- 20 | interface TalkableRobot { 21 | void talk(); 22 | } 23 | 24 | // --- Concrete Strategies for Talk --- 25 | class NormalTalk implements TalkableRobot { 26 | public void talk() { 27 | System.out.println("Talking normally..."); 28 | } 29 | } 30 | 31 | class NoTalk implements TalkableRobot { 32 | public void talk() { 33 | System.out.println("Cannot talk."); 34 | } 35 | } 36 | 37 | // --- Strategy Interface for Fly --- 38 | interface FlyableRobot { 39 | void fly(); 40 | } 41 | 42 | class NormalFly implements FlyableRobot { 43 | public void fly() { 44 | System.out.println("Flying normally..."); 45 | } 46 | } 47 | 48 | class NoFly implements FlyableRobot { 49 | public void fly() { 50 | System.out.println("Cannot fly."); 51 | } 52 | } 53 | 54 | // --- Robot Base Class --- 55 | abstract class Robot { 56 | protected WalkableRobot walkBehavior; 57 | protected TalkableRobot talkBehavior; 58 | protected FlyableRobot flyBehavior; 59 | 60 | public Robot(WalkableRobot w, TalkableRobot t, FlyableRobot f) { 61 | this.walkBehavior = w; 62 | this.talkBehavior = t; 63 | this.flyBehavior = f; 64 | } 65 | 66 | public void walk() { 67 | walkBehavior.walk(); 68 | } 69 | 70 | public void talk() { 71 | talkBehavior.talk(); 72 | } 73 | 74 | public void fly() { 75 | flyBehavior.fly(); 76 | } 77 | 78 | public abstract void projection(); // Abstract method for subclasses 79 | } 80 | 81 | // --- Concrete Robot Types --- 82 | class CompanionRobot extends Robot { 83 | public CompanionRobot(WalkableRobot w, TalkableRobot t, FlyableRobot f) { 84 | super(w, t, f); 85 | } 86 | 87 | public void projection() { 88 | System.out.println("Displaying friendly companion features..."); 89 | } 90 | } 91 | 92 | class WorkerRobot extends Robot { 93 | public WorkerRobot(WalkableRobot w, TalkableRobot t, FlyableRobot f) { 94 | super(w, t, f); 95 | } 96 | 97 | public void projection() { 98 | System.out.println("Displaying worker efficiency stats..."); 99 | } 100 | } 101 | 102 | // --- Main Function --- 103 | public class StrategyDesignPattern { 104 | public static void main(String[] args) { 105 | Robot robot1 = new CompanionRobot(new NormalWalk(), new NormalTalk(), new NoFly()); 106 | robot1.walk(); 107 | robot1.talk(); 108 | robot1.fly(); 109 | robot1.projection(); 110 | 111 | System.out.println("--------------------"); 112 | 113 | Robot robot2 = new WorkerRobot(new NoWalk(), new NoTalk(), new NormalFly()); 114 | robot2.walk(); 115 | robot2.talk(); 116 | robot2.fly(); 117 | robot2.projection(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Lecture 08/Standard UML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 08/Standard UML.png -------------------------------------------------------------------------------- /Lecture 09/C++ Code/FactoryMethod.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // Product Class and subclasses 6 | class Burger { 7 | public: 8 | virtual void prepare() = 0; // Pure virtual function 9 | virtual ~Burger() {} // Virtual destructor 10 | }; 11 | 12 | class BasicBurger : public Burger { 13 | public: 14 | void prepare() override { 15 | cout << "Preparing Basic Burger with bun, patty, and ketchup!" << endl; 16 | } 17 | }; 18 | 19 | class StandardBurger : public Burger { 20 | public: 21 | void prepare() override { 22 | cout << "Preparing Standard Burger with bun, patty, cheese, and lettuce!" << endl; 23 | } 24 | }; 25 | 26 | class PremiumBurger : public Burger { 27 | public: 28 | void prepare() override { 29 | cout << "Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!" << endl; 30 | } 31 | }; 32 | 33 | class BasicWheatBurger : public Burger { 34 | public: 35 | void prepare() override { 36 | cout << "Preparing Basic Wheat Burger with bun, patty, and ketchup!" << endl; 37 | } 38 | }; 39 | 40 | class StandardWheatBurger : public Burger { 41 | public: 42 | void prepare() override { 43 | cout << "Preparing Standard Wheat Burger with bun, patty, cheese, and lettuce!" << endl; 44 | } 45 | }; 46 | 47 | class PremiumWheatBurger : public Burger { 48 | public: 49 | void prepare() override { 50 | cout << "Preparing Premium Wheat Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!" << endl; 51 | } 52 | }; 53 | 54 | 55 | // Factory and its concretions 56 | class BurgerFactory { 57 | public: 58 | virtual Burger* createBurger(string& type) = 0; 59 | }; 60 | 61 | class SinghBurger : public BurgerFactory { 62 | public: 63 | Burger* createBurger(string& type) override { 64 | if (type == "basic") { 65 | return new BasicBurger(); 66 | } else if (type == "standard") { 67 | return new StandardBurger(); 68 | } else if (type == "premium") { 69 | return new PremiumBurger(); 70 | } else { 71 | cout << "Invalid burger type! " << endl; 72 | return nullptr; 73 | } 74 | } 75 | }; 76 | 77 | class KingBurger : public BurgerFactory { 78 | public: 79 | Burger* createBurger(string& type) override { 80 | if (type == "basic") { 81 | return new BasicWheatBurger(); 82 | } else if (type == "standard") { 83 | return new StandardWheatBurger(); 84 | } else if (type == "premium") { 85 | return new PremiumWheatBurger(); 86 | } else { 87 | cout << "Invalid burger type! " << endl; 88 | return nullptr; 89 | } 90 | } 91 | }; 92 | 93 | int main() { 94 | string type = "basic"; 95 | 96 | BurgerFactory* myFactory = new SinghBurger(); 97 | 98 | Burger* burger = myFactory->createBurger(type); 99 | 100 | burger->prepare(); 101 | 102 | return 0; 103 | } 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /Lecture 09/C++ Code/SimpleFactory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Burger { 6 | public: 7 | virtual void prepare() = 0; // Pure virtual function 8 | virtual ~Burger() {} // Virtual destructor 9 | }; 10 | 11 | class BasicBurger : public Burger { 12 | public: 13 | void prepare() override { 14 | cout << "Preparing Basic Burger with bun, patty, and ketchup!" << endl; 15 | } 16 | }; 17 | 18 | class StandardBurger : public Burger { 19 | public: 20 | void prepare() override { 21 | cout << "Preparing Standard Burger with bun, patty, cheese, and lettuce!" << endl; 22 | } 23 | }; 24 | 25 | class PremiumBurger : public Burger { 26 | public: 27 | void prepare() override { 28 | cout << "Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!" << endl; 29 | } 30 | }; 31 | 32 | class BurgerFactory { 33 | public: 34 | Burger* createBurger(string& type) { 35 | if (type == "basic") { 36 | return new BasicBurger(); 37 | } else if (type == "standard") { 38 | return new StandardBurger(); 39 | } else if (type == "premium") { 40 | return new PremiumBurger(); 41 | } else { 42 | cout << "Invalid burger type! " << endl; 43 | return nullptr; 44 | } 45 | } 46 | }; 47 | 48 | int main() { 49 | string type = "standard"; 50 | 51 | BurgerFactory* myBurgerFactory = new BurgerFactory(); 52 | 53 | Burger* burger = myBurgerFactory->createBurger(type); 54 | 55 | burger->prepare(); 56 | 57 | return 0; 58 | } 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Lecture 09/Java Code/FactoryMethod.java: -------------------------------------------------------------------------------- 1 | // Product Interface and subclasses 2 | interface Burger { 3 | void prepare(); 4 | } 5 | 6 | class BasicBurger implements Burger { 7 | public void prepare() { 8 | System.out.println("Preparing Basic Burger with bun, patty, and ketchup!"); 9 | } 10 | } 11 | 12 | class StandardBurger implements Burger { 13 | public void prepare() { 14 | System.out.println("Preparing Standard Burger with bun, patty, cheese, and lettuce!"); 15 | } 16 | } 17 | 18 | class PremiumBurger implements Burger { 19 | public void prepare() { 20 | System.out.println("Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!"); 21 | } 22 | } 23 | 24 | class BasicWheatBurger implements Burger { 25 | public void prepare() { 26 | System.out.println("Preparing Basic Wheat Burger with bun, patty, and ketchup!"); 27 | } 28 | } 29 | 30 | class StandardWheatBurger implements Burger { 31 | public void prepare() { 32 | System.out.println("Preparing Standard Wheat Burger with bun, patty, cheese, and lettuce!"); 33 | } 34 | } 35 | 36 | class PremiumWheatBurger implements Burger { 37 | public void prepare() { 38 | System.out.println("Preparing Premium Wheat Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!"); 39 | } 40 | } 41 | 42 | // Factory Interface and Concrete Factories 43 | interface BurgerFactory { 44 | Burger createBurger(String type); 45 | } 46 | 47 | class SinghBurger implements BurgerFactory { 48 | public Burger createBurger(String type) { 49 | if (type.equalsIgnoreCase("basic")) { 50 | return new BasicBurger(); 51 | } else if (type.equalsIgnoreCase("standard")) { 52 | return new StandardBurger(); 53 | } else if (type.equalsIgnoreCase("premium")) { 54 | return new PremiumBurger(); 55 | } else { 56 | System.out.println("Invalid burger type!"); 57 | return null; 58 | } 59 | } 60 | } 61 | 62 | class KingBurger implements BurgerFactory { 63 | public Burger createBurger(String type) { 64 | if (type.equalsIgnoreCase("basic")) { 65 | return new BasicWheatBurger(); 66 | } else if (type.equalsIgnoreCase("standard")) { 67 | return new StandardWheatBurger(); 68 | } else if (type.equalsIgnoreCase("premium")) { 69 | return new PremiumWheatBurger(); 70 | } else { 71 | System.out.println("Invalid burger type!"); 72 | return null; 73 | } 74 | } 75 | } 76 | 77 | // Main Class 78 | public class FactoryMethod { 79 | public static void main(String[] args) { 80 | String type = "basic"; 81 | 82 | BurgerFactory myFactory = new SinghBurger(); 83 | Burger burger = myFactory.createBurger(type); 84 | 85 | if (burger != null) { 86 | burger.prepare(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Lecture 09/Java Code/SimpleFactory.java: -------------------------------------------------------------------------------- 1 | // --- Burger Interface --- 2 | interface Burger { 3 | void prepare(); 4 | } 5 | 6 | // --- Concrete Burger Implementations --- 7 | class BasicBurger implements Burger { 8 | @Override 9 | public void prepare() { 10 | System.out.println("Preparing Basic Burger with bun, patty, and ketchup!"); 11 | } 12 | } 13 | 14 | class StandardBurger implements Burger { 15 | @Override 16 | public void prepare() { 17 | System.out.println("Preparing Standard Burger with bun, patty, cheese, and lettuce!"); 18 | } 19 | } 20 | 21 | class PremiumBurger implements Burger { 22 | @Override 23 | public void prepare() { 24 | System.out.println("Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!"); 25 | } 26 | } 27 | 28 | // --- Burger Factory --- 29 | class BurgerFactory { 30 | public Burger createBurger(String type) { 31 | if (type.equalsIgnoreCase("basic")) { 32 | return new BasicBurger(); 33 | } else if (type.equalsIgnoreCase("standard")) { 34 | return new StandardBurger(); 35 | } else if (type.equalsIgnoreCase("premium")) { 36 | return new PremiumBurger(); 37 | } else { 38 | System.out.println("Invalid burger type!"); 39 | return null; 40 | } 41 | } 42 | } 43 | 44 | // --- Main Class --- 45 | public class SimpleFactory { 46 | public static void main(String[] args) { 47 | String type = "standard"; 48 | 49 | BurgerFactory myBurgerFactory = new BurgerFactory(); 50 | 51 | Burger burger = myBurgerFactory.createBurger(type); 52 | 53 | if (burger != null) { 54 | burger.prepare(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Lecture 10/C++ Code/NoSingleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class NoSingleton { 6 | public: 7 | NoSingleton() { 8 | cout << "Singleton Constructor called. New Object created." << endl; 9 | } 10 | }; 11 | 12 | int main() { 13 | NoSingleton* s1 = new NoSingleton(); 14 | NoSingleton* s2 = new NoSingleton(); 15 | 16 | cout << (s1 == s2) << endl; 17 | } -------------------------------------------------------------------------------- /Lecture 10/C++ Code/SimpleSingleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Singleton { 6 | private: 7 | static Singleton* instance; 8 | 9 | Singleton() { 10 | cout << "Singleton Constructor called" << endl; 11 | } 12 | 13 | public: 14 | static Singleton* getInstance() { 15 | if(instance == nullptr) { 16 | instance = new Singleton(); 17 | } 18 | return instance; 19 | } 20 | }; 21 | 22 | // Initialize static member 23 | Singleton* Singleton::instance = nullptr; 24 | 25 | int main() { 26 | Singleton* s1 = Singleton::getInstance(); 27 | Singleton* s2 = Singleton::getInstance(); 28 | 29 | cout << (s1 == s2) << endl; 30 | } -------------------------------------------------------------------------------- /Lecture 10/C++ Code/ThreadSafeDoubleLockingSingleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class Singleton { 7 | private: 8 | static Singleton* instance; 9 | static mutex mtx; 10 | 11 | Singleton() { 12 | cout << "Singleton Constructor Called!" << endl; 13 | } 14 | 15 | public: 16 | // Double check locking.. 17 | static Singleton* getInstance() { 18 | if (instance == nullptr) { // First check (no locking) 19 | lock_guard lock(mtx); // Lock only if needed 20 | if (instance == nullptr) { // Second check (after acquiring lock) 21 | instance = new Singleton(); 22 | } 23 | } 24 | return instance; 25 | } 26 | }; 27 | 28 | // Initialize static members 29 | Singleton* Singleton::instance = nullptr; 30 | mutex Singleton::mtx; 31 | 32 | int main() { 33 | Singleton* s1 = Singleton::getInstance(); 34 | Singleton* s2 = Singleton::getInstance(); 35 | 36 | cout << (s1 == s2) << endl; 37 | } -------------------------------------------------------------------------------- /Lecture 10/C++ Code/ThreadSafeEagerSingleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class Singleton { 7 | private: 8 | static Singleton* instance; 9 | 10 | Singleton() { 11 | cout << "Singleton Constructor Called!" << endl; 12 | } 13 | 14 | public: 15 | static Singleton* getInstance() { 16 | return instance; 17 | } 18 | }; 19 | 20 | // Initialize static members 21 | Singleton* Singleton::instance = new Singleton(); 22 | 23 | int main() { 24 | Singleton* s1 = Singleton::getInstance(); 25 | Singleton* s2 = Singleton::getInstance(); 26 | 27 | cout << (s1 == s2) << endl; 28 | } -------------------------------------------------------------------------------- /Lecture 10/C++ Code/ThreadSafeLockingSingleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | class Singleton { 7 | private: 8 | static Singleton* instance; 9 | static mutex mtx; 10 | 11 | Singleton() { 12 | cout << "Singleton Constructor Called!" << endl; 13 | } 14 | 15 | public: 16 | static Singleton* getInstance() { 17 | lock_guard lock(mtx); // Lock for thread safety 18 | if (instance == nullptr) { 19 | instance = new Singleton(); 20 | } 21 | return instance; 22 | } 23 | }; 24 | 25 | // Initialize static members 26 | Singleton* Singleton::instance = nullptr; 27 | mutex Singleton::mtx; 28 | 29 | int main() { 30 | Singleton* s1 = Singleton::getInstance(); 31 | Singleton* s2 = Singleton::getInstance(); 32 | 33 | cout << (s1 == s2) << endl; 34 | } -------------------------------------------------------------------------------- /Lecture 10/Java Code/NoSingleton.java: -------------------------------------------------------------------------------- 1 | public class NoSingleton { 2 | public NoSingleton() { 3 | System.out.println("Singleton Constructor called. New Object created."); 4 | } 5 | 6 | public static void main(String[] args) { 7 | NoSingleton s1 = new NoSingleton(); 8 | NoSingleton s2 = new NoSingleton(); 9 | 10 | System.out.println(s1 == s2); 11 | } 12 | } -------------------------------------------------------------------------------- /Lecture 10/Java Code/SimpleSingleton.java: -------------------------------------------------------------------------------- 1 | public class SimpleSingleton { 2 | private static SimpleSingleton instance = null; 3 | 4 | private SimpleSingleton() { 5 | System.out.println("Singleton Constructor called"); 6 | } 7 | 8 | public static SimpleSingleton getInstance() { 9 | if (instance == null) { 10 | instance = new SimpleSingleton(); 11 | } 12 | return instance; 13 | } 14 | 15 | public static void main(String[] args) { 16 | SimpleSingleton s1 = SimpleSingleton.getInstance(); 17 | SimpleSingleton s2 = SimpleSingleton.getInstance(); 18 | 19 | System.out.println(s1 == s2); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Lecture 10/Java Code/ThreadSafeDoubleLockingSingleton.java: -------------------------------------------------------------------------------- 1 | public class ThreadSafeDoubleLockingSingleton { 2 | private static ThreadSafeDoubleLockingSingleton instance = null; 3 | 4 | private ThreadSafeDoubleLockingSingleton() { 5 | System.out.println("Singleton Constructor Called!"); 6 | } 7 | 8 | // Double check locking.. 9 | public static ThreadSafeDoubleLockingSingleton getInstance() { 10 | if (instance == null) { // First check (no locking) 11 | synchronized (ThreadSafeDoubleLockingSingleton.class) { // Lock only if needed 12 | if (instance == null) { // Second check (after acquiring lock) 13 | instance = new ThreadSafeDoubleLockingSingleton(); 14 | } 15 | } 16 | } 17 | return instance; 18 | } 19 | 20 | public static void main(String[] args) { 21 | ThreadSafeDoubleLockingSingleton s1 = ThreadSafeDoubleLockingSingleton.getInstance(); 22 | ThreadSafeDoubleLockingSingleton s2 = ThreadSafeDoubleLockingSingleton.getInstance(); 23 | 24 | System.out.println(s1 == s2); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Lecture 10/Java Code/ThreadSafeEagerSingleton.java: -------------------------------------------------------------------------------- 1 | public class ThreadSafeEagerSingleton { 2 | private static ThreadSafeEagerSingleton instance = new ThreadSafeEagerSingleton(); 3 | 4 | private ThreadSafeEagerSingleton() { 5 | System.out.println("Singleton Constructor Called!"); 6 | } 7 | 8 | public static ThreadSafeEagerSingleton getInstance() { 9 | return instance; 10 | } 11 | 12 | public static void main(String[] args) { 13 | ThreadSafeEagerSingleton s1 = ThreadSafeEagerSingleton.getInstance(); 14 | ThreadSafeEagerSingleton s2 = ThreadSafeEagerSingleton.getInstance(); 15 | 16 | System.out.println(s1 == s2); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lecture 10/Java Code/ThreadSafeLockingSingleton.java: -------------------------------------------------------------------------------- 1 | public class ThreadSafeLockingSingleton { 2 | private static ThreadSafeLockingSingleton instance = null; 3 | 4 | private ThreadSafeLockingSingleton() { 5 | System.out.println("Singleton Constructor Called!"); 6 | } 7 | 8 | public static ThreadSafeLockingSingleton getInstance() { 9 | synchronized (ThreadSafeLockingSingleton.class) { // Lock for thread safety 10 | if (instance == null) { 11 | instance = new ThreadSafeLockingSingleton(); 12 | } 13 | return instance; 14 | } 15 | } 16 | 17 | public static void main(String[] args) { 18 | ThreadSafeLockingSingleton s1 = ThreadSafeLockingSingleton.getInstance(); 19 | ThreadSafeLockingSingleton s2 = ThreadSafeLockingSingleton.getInstance(); 20 | 21 | System.out.println(s1 == s2); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/factories/NowOrderFactory.h: -------------------------------------------------------------------------------- 1 | #ifndef NOW_ORDER_FACTORY_H 2 | #define NOW_ORDER_FACTORY_H 3 | 4 | #include "OrderFactory.h" 5 | #include "../models/DeliveryOrder.h" 6 | #include "../models/PickupOrder.h" 7 | #include "../utils/TimeUtils.h" 8 | using namespace std; 9 | 10 | class NowOrderFactory : public OrderFactory { 11 | public: 12 | Order* createOrder(User* user, Cart* cart, Restaurant* restaurant, const vector& menuItems, 13 | PaymentStrategy* paymentStrategy, double totalCost, const string& orderType) override { 14 | Order* order = nullptr; 15 | if (orderType == "Delivery") { 16 | auto deliveryOrder = new DeliveryOrder(); 17 | deliveryOrder->setUserAddress(user->getAddress()); 18 | order = deliveryOrder; 19 | } 20 | else { 21 | auto pickupOrder = new PickupOrder(); 22 | pickupOrder->setRestaurantAddress(restaurant->getLocation()); 23 | order = pickupOrder; 24 | } 25 | order->setUser(user); 26 | order->setRestaurant(restaurant); 27 | order->setItems(menuItems); 28 | order->setPaymentStrategy(paymentStrategy); 29 | order->setScheduled(TimeUtils::getCurrentTime()); 30 | order->setTotal(totalCost); 31 | return order; 32 | } 33 | }; 34 | 35 | #endif // NOW_ORDER_FACTORY_H 36 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/factories/OrderFactory.h: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_FACTORY_H 2 | #define ORDER_FACTORY_H 3 | 4 | #include "../models/Order.h" 5 | #include "../models/Cart.h" 6 | #include "../models/Restaurant.h" 7 | #include "../strategies/PaymentStrategy.h" 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | class OrderFactory { 13 | public: 14 | virtual Order* createOrder(User* user, Cart* cart, Restaurant* restaurant, const vector& menuItems, 15 | PaymentStrategy* paymentStrategy, double totalCost, const string& orderType) = 0; 16 | virtual ~OrderFactory() {} 17 | }; 18 | 19 | #endif // ORDER_FACTORY_H 20 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/factories/ScheduledOrderFactory.h: -------------------------------------------------------------------------------- 1 | #ifndef SCHEDULED_ORDER_FACTORY_H 2 | #define SCHEDULED_ORDER_FACTORY_H 3 | 4 | #include "OrderFactory.h" 5 | #include "../models/DeliveryOrder.h" 6 | #include "../models/PickupOrder.h" 7 | #include "../utils/TimeUtils.h" 8 | using namespace std; 9 | 10 | class ScheduledOrderFactory : public OrderFactory { 11 | private: 12 | string scheduleTime; 13 | public: 14 | ScheduledOrderFactory(string scheduleTime) { 15 | this->scheduleTime = scheduleTime; 16 | } 17 | 18 | Order* createOrder(User* user, Cart* cart, Restaurant* restaurant, const vector& menuItems, 19 | PaymentStrategy* paymentStrategy, double totalCost, const string& orderType) override { 20 | Order* order = nullptr; 21 | 22 | if(orderType == "Delivery") { 23 | auto deliveryOrder = new DeliveryOrder(); 24 | deliveryOrder->setUserAddress(user->getAddress()); 25 | order = deliveryOrder; 26 | } 27 | else { 28 | auto pickupOrder = new PickupOrder(); 29 | pickupOrder->setRestaurantAddress(restaurant->getLocation()); 30 | } 31 | order->setUser(user); 32 | order->setRestaurant(restaurant); 33 | order->setItems(menuItems); 34 | order->setPaymentStrategy(paymentStrategy); 35 | order->setScheduled(scheduleTime); 36 | order->setTotal(totalCost); 37 | return order; 38 | } 39 | }; 40 | 41 | #endif // SCHEDULED_ORDER_FACTORY_H 42 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/folderStructure.txt: -------------------------------------------------------------------------------- 1 | OnlineFoodOrderingSystem/ 2 | │ 3 | ├── main.cpp # Composition root and entry point 4 | ├── TomatoApp.h # Facade class (main orchestrator) 5 | │ 6 | ├── models/ 7 | │ ├── MenuItem.h 8 | │ ├── Restaurant.h 9 | │ ├── User.h 10 | │ ├── Cart.h 11 | │ ├── Order.h # Abstract Order 12 | │ ├── DeliveryOrder.h 13 | │ ├── PickupOrder.h 14 | │ 15 | ├── managers/ 16 | │ ├── RestaurantManager.h 17 | │ ├── OrderManager.h 18 | │ 19 | ├── strategies/ 20 | │ ├── PaymentStrategy.h # Base class 21 | │ ├── CreditCardPaymentStrategy.h 22 | │ ├── UpiPaymentStrategy.h 23 | │ 24 | ├── factories/ 25 | │ ├── OrderFactory.h # Abstract factory 26 | │ ├── NowOrderFactory.h 27 | │ ├── ScheduledOrderFactory.h 28 | │ 29 | ├── services/ 30 | │ └── NotificationService.h 31 | │ 32 | ├── utils/ 33 | │ └── TimeUtils.h 34 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TomatoApp.h" 3 | using namespace std; 4 | 5 | int main() { 6 | // Create TomatoApp Object 7 | TomatoApp* tomato = new TomatoApp(); 8 | 9 | // Simulate a user coming in (Happy Flow) 10 | User* user = new User(101, "Aditya", "Delhi"); 11 | cout << "User: " << user->getName() << " is active." << endl; 12 | 13 | // User searches for restaurants by location 14 | vector restaurantList = tomato->searchRestaurants("Delhi"); 15 | 16 | if (restaurantList.empty()) { 17 | cout << "No restaurants found!" << endl; 18 | return 0; 19 | } 20 | cout << "Found Restaurants:" << endl; 21 | for (auto restaurant : restaurantList) { 22 | cout << " - " << restaurant->getName() << endl; 23 | } 24 | 25 | // User selects a restaurant 26 | tomato->selectRestaurant(user, restaurantList[0]); 27 | 28 | cout << "Selected restaurant: " << restaurantList[0]->getName() << endl; 29 | 30 | // User adds items to the cart 31 | tomato->addToCart(user, "P1"); 32 | tomato->addToCart(user, "P2"); 33 | 34 | tomato->printUserCart(user); 35 | 36 | // User checkout the cart 37 | Order* order = tomato->checkoutNow(user, "Delivery", new UpiPaymentStrategy("1234567890")); 38 | 39 | // User pay for the cart. If payment is success, notification is sent. 40 | tomato->payForOrder(user, order); 41 | 42 | // Cleanup Code. 43 | delete tomato; 44 | delete user; 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/managers/OrderManager.h: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_MANAGER_H 2 | #define ORDER_MANAGER_H 3 | 4 | #include 5 | #include 6 | #include "../models/Order.h" 7 | using namespace std; 8 | 9 | class OrderManager { 10 | private: 11 | vector orders; 12 | static OrderManager* instance; 13 | 14 | OrderManager() { 15 | // Private Constructor 16 | } 17 | 18 | public: 19 | static OrderManager* getInstance() { 20 | if (!instance) { 21 | instance = new OrderManager(); 22 | } 23 | return instance; 24 | } 25 | 26 | void addOrder(Order* order) { 27 | orders.push_back(order); 28 | } 29 | 30 | void listOrders() { 31 | cout << "\n--- All Orders ---" << endl; 32 | for (auto order : orders) { 33 | cout << order->getType() << " order for " << order->getUser()->getName() 34 | << " | Total: ₹" << order->getTotal() 35 | << " | At: " << order->getScheduled() << endl; 36 | } 37 | } 38 | }; 39 | 40 | OrderManager* OrderManager::instance = nullptr; 41 | 42 | #endif // ORDER_MANAGER_H 43 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/managers/RestaurantManager.h: -------------------------------------------------------------------------------- 1 | #ifndef RESTAURANT_MANAGER_H 2 | #define RESTAURANT_MANAGER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../models/Restaurant.h" 8 | using namespace std; 9 | 10 | class RestaurantManager { 11 | private: 12 | vector restaurants; 13 | static RestaurantManager* instance; 14 | 15 | RestaurantManager() { 16 | // private constructor 17 | } 18 | 19 | public: 20 | static RestaurantManager* getInstance() { 21 | if (!instance) { 22 | instance = new RestaurantManager(); 23 | } 24 | return instance; 25 | } 26 | 27 | void addRestaurant(Restaurant* r) { 28 | restaurants.push_back(r); 29 | } 30 | 31 | vector searchByLocation(string loc) { 32 | vector result; 33 | transform(loc.begin(), loc.end(), loc.begin(), ::tolower); 34 | for (auto r : restaurants) { 35 | string rl = r->getLocation(); 36 | transform(rl.begin(), rl.end(), rl.begin(), ::tolower); 37 | if (rl == loc) { 38 | result.push_back(r); 39 | } 40 | } 41 | return result; 42 | } 43 | }; 44 | 45 | RestaurantManager* RestaurantManager::instance = nullptr; 46 | 47 | #endif // RESTAURANT_MANAGER_H 48 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/Cart.h: -------------------------------------------------------------------------------- 1 | #ifndef CART_H 2 | #define CART_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../models/MenuItem.h" 8 | #include "../models/Restaurant.h" 9 | 10 | using namespace std; 11 | 12 | class Cart { 13 | private: 14 | Restaurant* restaurant; 15 | vector items; 16 | 17 | public: 18 | Cart() { 19 | restaurant = nullptr; 20 | } 21 | 22 | void addItem(const MenuItem& item) { 23 | if (!restaurant) { 24 | cerr << "Cart: Set a restaurant before adding items." << endl; 25 | return; 26 | } 27 | items.push_back(item); 28 | } 29 | 30 | double getTotalCost() const { 31 | double sum = 0; 32 | for (const auto& it : items) { 33 | sum += it.getPrice(); 34 | } 35 | return sum; 36 | } 37 | 38 | bool isEmpty() { 39 | return (!restaurant || items.empty()); 40 | } 41 | 42 | void clear() { 43 | items.clear(); 44 | restaurant = nullptr; 45 | } 46 | 47 | // Getters and Setters 48 | void setRestaurant(Restaurant* r) { 49 | restaurant = r; 50 | } 51 | 52 | Restaurant* getRestaurant() const { 53 | return restaurant; 54 | } 55 | 56 | const vector& getItems() const { 57 | return items; 58 | } 59 | }; 60 | 61 | #endif // CART_H 62 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/DeliveryOrder.h: -------------------------------------------------------------------------------- 1 | #ifndef DELIVERY_ORDER_H 2 | #define DELIVERY_ORDER_H 3 | 4 | #include "Order.h" 5 | using namespace std; 6 | 7 | class DeliveryOrder : public Order { 8 | private: 9 | string userAddress; 10 | 11 | public: 12 | DeliveryOrder() { 13 | userAddress = ""; 14 | } 15 | 16 | string getType() const override { 17 | return "Delivery"; 18 | } 19 | 20 | //Getter and Setters 21 | void setUserAddress(const string& addr) { 22 | userAddress = addr; 23 | } 24 | 25 | string getUserAddress() const { 26 | return userAddress; 27 | } 28 | }; 29 | 30 | #endif // DELIVERY_ORDER_H 31 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/MenuItem.h: -------------------------------------------------------------------------------- 1 | #ifndef MENUITEM_H 2 | #define MENUITEM_H 3 | 4 | #include 5 | using namespace std; 6 | 7 | class MenuItem { 8 | private: 9 | string code; 10 | string name; 11 | int price; 12 | 13 | public: 14 | MenuItem(const string& code, const string& name, int price) { 15 | this->code = code; 16 | this->name = name; 17 | this->price = price; 18 | } 19 | 20 | //Getters and setters 21 | string getCode() const { 22 | return code; 23 | } 24 | 25 | void setCode(const string &c) { 26 | code = c; 27 | } 28 | 29 | string getName() const { 30 | return name; 31 | } 32 | 33 | void setName(const string &n) { 34 | name = n; 35 | } 36 | 37 | int getPrice() const { 38 | return price; 39 | } 40 | 41 | void setPrice(int p) { 42 | price = p; 43 | } 44 | }; 45 | 46 | #endif // MENUITEM_H 47 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/Order.h: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_H 2 | #define ORDER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "User.h" 8 | #include "Restaurant.h" 9 | #include "MenuItem.h" 10 | #include "../strategies/PaymentStrategy.h" 11 | #include "../utils/TimeUtils.h" 12 | using namespace std; 13 | 14 | class Order { 15 | protected: 16 | static int nextOrderId; 17 | int orderId; 18 | User* user; 19 | Restaurant* restaurant; 20 | vector items; 21 | PaymentStrategy* paymentStrategy; 22 | double total; 23 | string scheduled; 24 | 25 | public: 26 | Order() { 27 | user = nullptr; 28 | restaurant = nullptr; 29 | paymentStrategy = nullptr; 30 | total = 0.0; 31 | scheduled = ""; 32 | orderId = ++nextOrderId; 33 | } 34 | 35 | virtual ~Order() { 36 | delete paymentStrategy; 37 | } 38 | 39 | bool processPayment() { 40 | if (paymentStrategy) { 41 | paymentStrategy->pay(total); 42 | return true; 43 | } else { 44 | cout << "Please choose a payment mode first" << endl; 45 | return false; 46 | } 47 | } 48 | 49 | virtual string getType() const = 0; 50 | 51 | //Getter and Setters 52 | int getOrderId() const { 53 | return orderId; 54 | } 55 | 56 | void setUser(User* u) { 57 | user = u; 58 | } 59 | 60 | User* getUser() const { 61 | return user; 62 | } 63 | 64 | void setRestaurant(Restaurant* r) { 65 | restaurant = r; 66 | } 67 | 68 | Restaurant* getRestaurant() const { 69 | return restaurant; 70 | } 71 | 72 | void setItems(const vector& its) { 73 | items = its; 74 | total = 0; 75 | for (auto &i : items) { 76 | total += i.getPrice(); 77 | } 78 | } 79 | 80 | const vector& getItems() const { 81 | return items; 82 | } 83 | 84 | void setPaymentStrategy(PaymentStrategy* p) { 85 | paymentStrategy = p; 86 | } 87 | 88 | void setScheduled(const string& s) { 89 | scheduled = s; 90 | } 91 | 92 | string getScheduled() const { 93 | return scheduled; 94 | } 95 | 96 | double getTotal() const { 97 | return total; 98 | } 99 | 100 | void setTotal(int total) { 101 | this->total = total; 102 | } 103 | }; 104 | 105 | int Order::nextOrderId = 0; 106 | 107 | #endif // ORDER_H 108 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/PickupOrder.h: -------------------------------------------------------------------------------- 1 | #ifndef PICKUP_ORDER_H 2 | #define PICKUP_ORDER_H 3 | 4 | #include "Order.h" 5 | using namespace std; 6 | 7 | class PickupOrder : public Order { 8 | private: 9 | string restaurantAddress; 10 | 11 | public: 12 | PickupOrder() { 13 | restaurantAddress = ""; 14 | } 15 | 16 | string getType() const override { 17 | return "Pickup"; 18 | } 19 | 20 | //Getter and Setters 21 | void setRestaurantAddress(const string& addr) { 22 | restaurantAddress = addr; 23 | } 24 | 25 | string getRestaurantAddress() const { 26 | return restaurantAddress; 27 | } 28 | }; 29 | 30 | #endif // PICKUP_ORDER_H 31 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/Restaurant.h: -------------------------------------------------------------------------------- 1 | #ifndef RESTAURANT_H 2 | #define RESTAURANT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "MenuItem.h" 8 | using namespace std; 9 | 10 | class Restaurant { 11 | private: 12 | static int nextRestaurantId; 13 | int restaurantId; 14 | string name; 15 | string location; 16 | vector menu; 17 | 18 | public: 19 | Restaurant(const string& name, const string& location) { 20 | this->name = name; 21 | this->location = location; 22 | this->restaurantId = ++nextRestaurantId; 23 | } 24 | 25 | ~Restaurant() { 26 | // Optional: just for clarity or debug 27 | cout << "Destroying Restaurant: " << name << ", and clearing its menu." << endl; 28 | menu.clear(); 29 | } 30 | 31 | //Getters and setters 32 | string getName() const { 33 | return name; 34 | } 35 | 36 | void setName(const string &n) { 37 | name = n; 38 | } 39 | 40 | string getLocation() const { 41 | return location; 42 | } 43 | 44 | void setLocation(const string &loc) { 45 | location = loc; 46 | } 47 | 48 | void addMenuItem(const MenuItem &item) { 49 | menu.push_back(item); 50 | } 51 | 52 | const vector& getMenu() const { 53 | return menu; 54 | } 55 | }; 56 | 57 | int Restaurant::nextRestaurantId = 0; 58 | 59 | #endif // RESTAURANT_H 60 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/models/User.h: -------------------------------------------------------------------------------- 1 | #ifndef USER_H 2 | #define USER_H 3 | 4 | #include 5 | #include "Cart.h" 6 | using namespace std; 7 | 8 | class User { 9 | private: 10 | int userId; 11 | string name; 12 | string address; 13 | Cart* cart; 14 | 15 | public: 16 | User(int userId, const string& name, const string& address) { 17 | this->userId = userId; 18 | this->name = name; 19 | this->address = address; 20 | cart = new Cart(); 21 | } 22 | 23 | ~User() { 24 | delete cart; 25 | } 26 | 27 | // Getters and Setters 28 | string getName() const { 29 | return name; 30 | } 31 | 32 | void setName(const string &n) { 33 | name = n; 34 | } 35 | 36 | string getAddress() const { 37 | return address; 38 | } 39 | 40 | void setAddress(const string &a) { 41 | address = a; 42 | } 43 | 44 | Cart* getCart() const { 45 | return cart; 46 | } 47 | }; 48 | 49 | #endif // USER_H 50 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/services/NotificationService.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTIFICATION_SERVICE_H 2 | #define NOTIFICATION_SERVICE_H 3 | 4 | #include 5 | #include "../models/Order.h" 6 | using namespace std; 7 | 8 | class NotificationService { 9 | public: 10 | static void notify(Order* order) { 11 | cout << "\nNotification: New " << order->getType() << " order placed!" << endl; 12 | cout << "---------------------------------------------" << endl; 13 | cout << "Order ID: " << order->getOrderId() << endl; 14 | cout << "Customer: " << order->getUser()->getName() << endl; 15 | cout << "Restaurant: " << order->getRestaurant()->getName() << endl; 16 | cout << "Items Ordered:\n"; 17 | 18 | const vector& items = order->getItems(); 19 | for (const auto& item : items) { 20 | cout << " - " << item.getName() << " (₹" << item.getPrice() << ")\n"; 21 | } 22 | 23 | cout << "Total: ₹" << order->getTotal() << endl; 24 | cout << "Scheduled For: " << order->getScheduled() << endl; 25 | cout << "Payment: Done" << endl; 26 | cout << "---------------------------------------------" << endl; 27 | } 28 | }; 29 | 30 | #endif // NOTIFICATION_SERVICE_H 31 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/strategies/CreditCartPaymentStrategy.h: -------------------------------------------------------------------------------- 1 | #ifndef CREDIT_CARD_PAYMENT_STRATEGY_H 2 | #define CREDIT_CARD_PAYMENT_STRATEGY_H 3 | 4 | #include "PaymentStrategy.h" 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | class CreditCardPaymentStrategy : public PaymentStrategy { 11 | private: 12 | string cardNumber; 13 | public: 14 | CreditCardPaymentStrategy(const string& card) { 15 | cardNumber = card; 16 | } 17 | 18 | void pay(double amount) override { 19 | cout << "Paid ₹" << amount << " using Credit Card (" << cardNumber << ")" << endl; 20 | } 21 | }; 22 | 23 | #endif // CREDIT_CARD_PAYMENT_STRATEGY_H 24 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/strategies/PaymentStrategy.h: -------------------------------------------------------------------------------- 1 | #ifndef PAYMENT_STRATEGY_H 2 | #define PAYMENT_STRATEGY_H 3 | 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | class PaymentStrategy { 9 | public: 10 | virtual void pay(double amount) = 0; 11 | virtual ~PaymentStrategy() {} 12 | }; 13 | 14 | #endif // PAYMENT_STRATEGY_H 15 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/strategies/UpiPaymentStrategy.h: -------------------------------------------------------------------------------- 1 | #ifndef UPI_PAYMENT_STRATEGY_H 2 | #define UPI_PAYMENT_STRATEGY_H 3 | 4 | #include "PaymentStrategy.h" 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | class UpiPaymentStrategy : public PaymentStrategy { 11 | private: 12 | string mobile; 13 | public: 14 | UpiPaymentStrategy(const string& mob) { 15 | mobile = mob; 16 | } 17 | 18 | void pay(double amount) override { 19 | cout << "Paid ₹" << amount << " using UPI (" << mobile << ")" << endl; 20 | } 21 | }; 22 | 23 | #endif // UPI_PAYMENT_STRATEGY_H 24 | -------------------------------------------------------------------------------- /Lecture 11/C++ Code/Tomato/utils/TimeUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef TIME_UTILS_H 2 | #define TIME_UTILS_H 3 | 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | class TimeUtils { 9 | public: 10 | static string getCurrentTime() { 11 | time_t now = time(0); 12 | char* dt = ctime(&now); 13 | string s(dt); 14 | if (!s.empty() && s.back() == '\n') 15 | s.pop_back(); 16 | return s; 17 | } 18 | }; 19 | 20 | #endif // TIME_UTILS_H 21 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/Main.java: -------------------------------------------------------------------------------- 1 | import models.*; 2 | import strategies.*; 3 | 4 | public class Main { 5 | public static void main(String[] args) { 6 | // Simulating a happy flow 7 | // Create TomatoApp Object 8 | TomatoApp tomato = new TomatoApp(); 9 | 10 | // Simulate a user coming in (Happy Flow) 11 | User user = new User(101, "Aditya", "Delhi"); 12 | System.out.println("User: " + user.getName() + " is active."); 13 | 14 | // User searches for restaurants by location 15 | java.util.List restaurantList = tomato.searchRestaurants("Delhi"); 16 | 17 | if (restaurantList.isEmpty()) { 18 | System.out.println("No restaurants found!"); 19 | return; 20 | } 21 | 22 | System.out.println("Found Restaurants:"); 23 | for (Restaurant restaurant : restaurantList) { 24 | System.out.println(" - " + restaurant.getName()); 25 | } 26 | 27 | // User selects a restaurant 28 | tomato.selectRestaurant(user, restaurantList.get(0)); 29 | System.out.println("Selected restaurant: " + restaurantList.get(0).getName()); 30 | 31 | // User adds items to the cart 32 | tomato.addToCart(user, "P1"); 33 | tomato.addToCart(user, "P2"); 34 | 35 | tomato.printUserCart(user); 36 | 37 | // User checkout the cart 38 | Order order = tomato.checkoutNow(user, "Delivery", new UpiPaymentStrategy("1234567890")); 39 | 40 | // User pays for the cart. If payment is successful, notification is sent. 41 | tomato.payForOrder(user, order); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/factories/NowOrderFactory.java: -------------------------------------------------------------------------------- 1 | package factories; 2 | 3 | import java.util.List; 4 | import models.*; 5 | import strategies.*; 6 | import utils.*; 7 | 8 | public class NowOrderFactory implements OrderFactory { 9 | @Override 10 | public Order createOrder(User user, Cart cart, Restaurant restaurant, List menuItems, 11 | PaymentStrategy paymentStrategy, double totalCost, String orderType) { 12 | Order order = null; 13 | 14 | if (orderType.equals("Delivery")) { 15 | DeliveryOrder deliveryOrder = new DeliveryOrder(); 16 | deliveryOrder.setUserAddress(user.getAddress()); 17 | order = deliveryOrder; 18 | } else { 19 | PickupOrder pickupOrder = new PickupOrder(); 20 | pickupOrder.setRestaurantAddress(restaurant.getLocation()); 21 | order = pickupOrder; 22 | } 23 | 24 | order.setUser(user); 25 | order.setRestaurant(restaurant); 26 | order.setItems(menuItems); 27 | order.setPaymentStrategy(paymentStrategy); 28 | order.setScheduled(TimeUtils.getCurrentTime()); 29 | order.setTotal(totalCost); 30 | return order; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/factories/OrderFactory.java: -------------------------------------------------------------------------------- 1 | package factories; 2 | 3 | import java.util.List; 4 | import models.*; 5 | import strategies.*; 6 | 7 | public interface OrderFactory { 8 | Order createOrder(User user, Cart cart, Restaurant restaurant, List menuItems, 9 | PaymentStrategy paymentStrategy, double totalCost, String orderType); 10 | } -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/factories/ScheduledOrderFactory.java: -------------------------------------------------------------------------------- 1 | package factories; 2 | 3 | import java.util.List; 4 | import models.*; 5 | import strategies.*; 6 | 7 | public class ScheduledOrderFactory implements OrderFactory { 8 | private String scheduleTime; 9 | 10 | public ScheduledOrderFactory(String scheduleTime) { 11 | this.scheduleTime = scheduleTime; 12 | } 13 | 14 | @Override 15 | public Order createOrder(User user, Cart cart, Restaurant restaurant, List menuItems, 16 | PaymentStrategy paymentStrategy, double totalCost, String orderType) { 17 | Order order = null; 18 | 19 | if (orderType.equals("Delivery")) { 20 | DeliveryOrder deliveryOrder = new DeliveryOrder(); 21 | deliveryOrder.setUserAddress(user.getAddress()); 22 | order = deliveryOrder; 23 | } else { 24 | PickupOrder pickupOrder = new PickupOrder(); 25 | pickupOrder.setRestaurantAddress(restaurant.getLocation()); 26 | order = pickupOrder; 27 | } 28 | 29 | order.setUser(user); 30 | order.setRestaurant(restaurant); 31 | order.setItems(menuItems); 32 | order.setPaymentStrategy(paymentStrategy); 33 | order.setScheduled(scheduleTime); 34 | order.setTotal(totalCost); 35 | return order; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/folderStructure.txt: -------------------------------------------------------------------------------- 1 | OnlineFoodOrderingSystem/ 2 | │ 3 | ├── Main.java # Composition root and entry point 4 | ├── TomatoApp.java # Facade class (main orcjavaestrator) 5 | │ 6 | ├── models/ 7 | │ ├── MenuItem.java 8 | │ ├── Restaurant.java 9 | │ ├── User.java 10 | │ ├── Cart.java 11 | │ ├── Order.java # Abstract Order 12 | │ ├── DeliveryOrder.java 13 | │ ├── PickupOrder.java 14 | │ 15 | ├── managers/ 16 | │ ├── RestaurantManager.java 17 | │ ├── OrderManager.java 18 | │ 19 | ├── strategies/ 20 | │ ├── PaymentStrategy.java # Base class 21 | │ ├── CreditCardPaymentStrategy.java 22 | │ ├── UpiPaymentStrategy.java 23 | │ 24 | ├── factories/ 25 | │ ├── OrderFactory.java # Abstract factory 26 | │ ├── NowOrderFactory.java 27 | │ ├── ScheduledOrderFactory.java 28 | │ 29 | ├── services/ 30 | │ └── NotificationService.java 31 | │ 32 | ├── utils/ 33 | │ └── TimeUtils.java 34 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/managers/OrderManager.java: -------------------------------------------------------------------------------- 1 | package managers; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import models.*; 6 | 7 | public class OrderManager { 8 | private List orders = new ArrayList<>(); 9 | private static OrderManager instance = null; 10 | 11 | private OrderManager() { 12 | // Private Constructor 13 | } 14 | 15 | public static OrderManager getInstance() { 16 | if (instance == null) { 17 | instance = new OrderManager(); 18 | } 19 | return instance; 20 | } 21 | 22 | public void addOrder(Order order) { 23 | orders.add(order); 24 | } 25 | 26 | public void listOrders() { 27 | System.out.println("\n--- All Orders ---"); 28 | for (Order order : orders) { 29 | System.out.println(order.getType() + " order for " + order.getUser().getName() 30 | + " | Total: ₹" + order.getTotal() 31 | + " | At: " + order.getScheduled()); 32 | } 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/managers/RestaurantManager.java: -------------------------------------------------------------------------------- 1 | package managers; 2 | 3 | import models.*; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | // Singleton 8 | public class RestaurantManager { 9 | private List restaurants = new ArrayList<>(); 10 | private static RestaurantManager instance = null; 11 | 12 | private RestaurantManager() { 13 | // private constructor 14 | } 15 | 16 | public static RestaurantManager getInstance() { 17 | if (instance == null) { 18 | instance = new RestaurantManager(); 19 | } 20 | return instance; 21 | } 22 | 23 | public void addRestaurant(Restaurant r) { 24 | restaurants.add(r); 25 | } 26 | 27 | public List searchByLocation(String loc) { 28 | List result = new ArrayList<>(); 29 | loc = loc.toLowerCase(); 30 | for (Restaurant r : restaurants) { 31 | String rl = r.getLocation().toLowerCase(); 32 | if (rl.equals(loc)) { 33 | result.add(r); 34 | } 35 | } 36 | return result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/Cart.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Cart { 7 | private Restaurant restaurant; 8 | private List items = new ArrayList<>(); 9 | 10 | public Cart() { 11 | restaurant = null; 12 | } 13 | 14 | public void addItem(MenuItem item) { 15 | if (restaurant == null) { 16 | System.err.println("Cart: Set a restaurant before adding items."); 17 | return; 18 | } 19 | items.add(item); 20 | } 21 | 22 | public double getTotalCost() { 23 | double sum = 0; 24 | for (MenuItem it : items) { 25 | sum += it.getPrice(); 26 | } 27 | return sum; 28 | } 29 | 30 | public boolean isEmpty() { 31 | return restaurant == null || items.isEmpty(); 32 | } 33 | 34 | public void clear() { 35 | items.clear(); 36 | restaurant = null; 37 | } 38 | 39 | public void setRestaurant(Restaurant r) { 40 | restaurant = r; 41 | } 42 | 43 | public Restaurant getRestaurant() { 44 | return restaurant; 45 | } 46 | 47 | public List getItems() { 48 | return items; 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/DeliveryOrder.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | public class DeliveryOrder extends Order { 4 | private String userAddress; 5 | 6 | public DeliveryOrder() { 7 | userAddress = ""; 8 | } 9 | 10 | @Override 11 | public String getType() { 12 | return "Delivery"; 13 | } 14 | 15 | public void setUserAddress(String addr) { 16 | userAddress = addr; 17 | } 18 | 19 | public String getUserAddress() { 20 | return userAddress; 21 | } 22 | 23 | // Implement remaining Order methods with actual fields 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/MenuItem.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | public class MenuItem { 4 | private String code; 5 | private String name; 6 | private int price; 7 | 8 | public MenuItem(String code, String name, int price) { 9 | this.code = code; 10 | this.name = name; 11 | this.price = price; 12 | } 13 | 14 | public String getCode() { 15 | return code; 16 | } 17 | 18 | public void setCode(String c) { 19 | code = c; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String n) { 27 | name = n; 28 | } 29 | 30 | public int getPrice() { 31 | return price; 32 | } 33 | 34 | public void setPrice(int p) { 35 | price = p; 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/Order.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | import java.util.List; 4 | import strategies.*; 5 | 6 | public abstract class Order { 7 | private static int nextOrderId = 0; 8 | 9 | protected int orderId; 10 | protected User user; 11 | protected Restaurant restaurant; 12 | protected List items; 13 | protected PaymentStrategy paymentStrategy; 14 | protected double total; 15 | protected String scheduled; 16 | 17 | public Order() { 18 | this.user = null; 19 | this.restaurant = null; 20 | this.paymentStrategy = null; 21 | this.total = 0.0; 22 | this.scheduled = ""; 23 | this.orderId = ++nextOrderId; 24 | } 25 | 26 | public boolean processPayment() { 27 | if (paymentStrategy != null) { 28 | paymentStrategy.pay(total); 29 | return true; 30 | } else { 31 | System.out.println("Please choose a payment mode first"); 32 | return false; 33 | } 34 | } 35 | 36 | public abstract String getType(); 37 | 38 | // Getters and Setters 39 | public int getOrderId() { 40 | return orderId; 41 | } 42 | 43 | public void setUser(User u) { 44 | user = u; 45 | } 46 | 47 | public User getUser() { 48 | return user; 49 | } 50 | 51 | public void setRestaurant(Restaurant r) { 52 | restaurant = r; 53 | } 54 | 55 | public Restaurant getRestaurant() { 56 | return restaurant; 57 | } 58 | 59 | public void setItems(List its) { 60 | items = its; 61 | total = 0; 62 | for (MenuItem i : items) { 63 | total += i.getPrice(); 64 | } 65 | } 66 | 67 | public List getItems() { 68 | return items; 69 | } 70 | 71 | public void setPaymentStrategy(PaymentStrategy p) { 72 | paymentStrategy = p; 73 | } 74 | 75 | public void setScheduled(String s) { 76 | scheduled = s; 77 | } 78 | 79 | public String getScheduled() { 80 | return scheduled; 81 | } 82 | 83 | public double getTotal() { 84 | return total; 85 | } 86 | 87 | public void setTotal(double total) { 88 | this.total = total; 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/PickupOrder.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | public class PickupOrder extends Order { 4 | private String restaurantAddress; 5 | 6 | public PickupOrder() { 7 | restaurantAddress = ""; 8 | } 9 | 10 | @Override 11 | public String getType() { 12 | return "Pickup"; 13 | } 14 | 15 | public void setRestaurantAddress(String addr) { 16 | restaurantAddress = addr; 17 | } 18 | 19 | public String getRestaurantAddress() { 20 | return restaurantAddress; 21 | } 22 | 23 | // Implement remaining Order methods with actual fields 24 | } 25 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/Restaurant.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Restaurant { 7 | private static int nextRestaurantId = 0; 8 | private int restaurantId; 9 | private String name; 10 | private String location; 11 | private List menu = new ArrayList<>(); 12 | 13 | public Restaurant(String name, String location) { 14 | this.name = name; 15 | this.location = location; 16 | this.restaurantId = ++nextRestaurantId; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String n) { 24 | name = n; 25 | } 26 | 27 | public String getLocation() { 28 | return location; 29 | } 30 | 31 | public void setLocation(String loc) { 32 | location = loc; 33 | } 34 | 35 | public void addMenuItem(MenuItem item) { 36 | menu.add(item); 37 | } 38 | 39 | public List getMenu() { 40 | return menu; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/models/User.java: -------------------------------------------------------------------------------- 1 | package models; 2 | 3 | public class User { 4 | private int userId; 5 | private String name; 6 | private String address; 7 | private Cart cart; 8 | 9 | public User(int userId, String name, String address) { 10 | this.userId = userId; 11 | this.name = name; 12 | this.address = address; 13 | this.cart = new Cart(); 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public void setName(String n) { 21 | name = n; 22 | } 23 | 24 | public String getAddress() { 25 | return address; 26 | } 27 | 28 | public void setAddress(String a) { 29 | address = a; 30 | } 31 | 32 | public Cart getCart() { 33 | return cart; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/services/NotificationService.java: -------------------------------------------------------------------------------- 1 | package services; 2 | 3 | import java.util.List; 4 | import models.*; 5 | 6 | public class NotificationService { 7 | public static void notify(Order order) { 8 | System.out.println("\nNotification: New " + order.getType() + " order placed!"); 9 | System.out.println("---------------------------------------------"); 10 | System.out.println("Order ID: " + order.getOrderId()); 11 | System.out.println("Customer: " + order.getUser().getName()); 12 | System.out.println("Restaurant: " + order.getRestaurant().getName()); 13 | System.out.println("Items Ordered:"); 14 | 15 | List items = order.getItems(); 16 | for (MenuItem item : items) { 17 | System.out.println(" - " + item.getName() + " (₹" + item.getPrice() + ")"); 18 | } 19 | 20 | System.out.println("Total: ₹" + order.getTotal()); 21 | System.out.println("Scheduled For: " + order.getScheduled()); 22 | System.out.println("Payment: Done"); 23 | System.out.println("---------------------------------------------"); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/strategies/CreditCardPaymentStrategy.java: -------------------------------------------------------------------------------- 1 | package strategies; 2 | 3 | public class CreditCardPaymentStrategy implements PaymentStrategy { 4 | private String cardNumber; 5 | 6 | public CreditCardPaymentStrategy(String card) { 7 | this.cardNumber = card; 8 | } 9 | 10 | @Override 11 | public void pay(double amount) { 12 | System.out.println("Paid ₹" + amount + " using Credit Card (" + cardNumber + ")"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/strategies/PaymentStrategy.java: -------------------------------------------------------------------------------- 1 | package strategies; 2 | 3 | public interface PaymentStrategy { 4 | void pay(double amount); 5 | } 6 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/strategies/UpiPaymentStrategy.java: -------------------------------------------------------------------------------- 1 | package strategies; 2 | 3 | public class UpiPaymentStrategy implements PaymentStrategy { 4 | private String mobile; 5 | 6 | public UpiPaymentStrategy(String mob) { 7 | this.mobile = mob; 8 | } 9 | 10 | @Override 11 | public void pay(double amount) { 12 | System.out.println("Paid ₹" + amount + " using UPI (" + mobile + ")"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Lecture 11/Java Code/Tomato/utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | public class TimeUtils { 7 | public static String getCurrentTime() { 8 | LocalDateTime now = LocalDateTime.now(); 9 | return now.format(DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy")); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Lecture 11/UML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 11/UML.png -------------------------------------------------------------------------------- /Lecture 12/Java Code/ObserverDesignPattern.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | 4 | interface ISubscriber { 5 | void update(); 6 | } 7 | 8 | // Observable interface: a YouTube channel interface 9 | interface IChannel { 10 | void subscribe(ISubscriber subscriber); 11 | void unsubscribe(ISubscriber subscriber); 12 | void notifySubscribers(); 13 | } 14 | 15 | // Concrete Subject: a YouTube channel that observers can subscribe to 16 | class Channel implements IChannel { 17 | private List subscribers; 18 | private String name; 19 | private String latestVideo; 20 | 21 | public Channel(String name) { 22 | this.name = name; 23 | this.subscribers = new ArrayList<>(); 24 | } 25 | 26 | @Override 27 | public void subscribe(ISubscriber subscriber) { 28 | if (!subscribers.contains(subscriber)) { 29 | subscribers.add(subscriber); 30 | } 31 | } 32 | 33 | @Override 34 | public void unsubscribe(ISubscriber subscriber) { 35 | subscribers.remove(subscriber); 36 | } 37 | 38 | @Override 39 | public void notifySubscribers() { 40 | for (ISubscriber sub : subscribers) { 41 | sub.update(); 42 | } 43 | } 44 | 45 | public void uploadVideo(String title) { 46 | latestVideo = title; 47 | System.out.println("\n[" + name + " uploaded \"" + title + "\"]"); 48 | notifySubscribers(); 49 | } 50 | 51 | public String getVideoData() { 52 | return "\nCheckout our new Video : " + latestVideo + "\n"; 53 | } 54 | } 55 | 56 | // Concrete Observer: represents a subscriber to the channel 57 | class Subscriber implements ISubscriber { 58 | private String name; 59 | private Channel channel; 60 | 61 | public Subscriber(String name, Channel channel) { 62 | this.name = name; 63 | this.channel = channel; 64 | } 65 | 66 | @Override 67 | public void update() { 68 | System.out.println("Hey " + name + "," + channel.getVideoData()); 69 | } 70 | } 71 | 72 | public class ObserverDesignPattern { 73 | public static void main(String[] args) { 74 | // Create a channel and subscribers 75 | Channel channel = new Channel("CoderArmy"); 76 | 77 | Subscriber subs1 = new Subscriber("Varun", channel); 78 | Subscriber subs2 = new Subscriber("Tarun", channel); 79 | 80 | // Varun and Tarun subscribe to CoderArmy 81 | channel.subscribe(subs1); 82 | channel.subscribe(subs2); 83 | 84 | // Upload a video: both Varun and Tarun are notified 85 | channel.uploadVideo("Observer Pattern Tutorial"); 86 | 87 | // Varun unsubscribes; Tarun remains subscribed 88 | channel.unsubscribe(subs1); 89 | 90 | // Upload another video: only Tarun is notified 91 | channel.uploadVideo("Decorator Pattern Tutorial"); 92 | } 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /Lecture 12/Standard UML.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 12/Standard UML.jpg -------------------------------------------------------------------------------- /Lecture 13/C++ Code/DecoratorPattern.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // Component Interface: defines a common interface for Mario and all power-up decorators. 7 | class Character { 8 | public: 9 | virtual string getAbilities() const = 0; 10 | virtual ~Character() {} // Virtual destructor 11 | }; 12 | 13 | // Concrete Component: Basic Mario character with no power-ups. 14 | class Mario : public Character { 15 | public: 16 | string getAbilities() const override { 17 | return "Mario"; 18 | } 19 | }; 20 | 21 | // Abstract Decorator: CharacterDecorator "is-a" Charatcer and "has-a" Character. 22 | class CharacterDecorator : public Character { 23 | protected: 24 | Character* character; // Wrapped component 25 | public: 26 | CharacterDecorator(Character* c){ 27 | this->character = c; 28 | } 29 | 30 | }; 31 | 32 | // Concrete Decorator: Height-Increasing Power-Up. 33 | class HeightUp : public CharacterDecorator { 34 | public: 35 | HeightUp(Character* c) : CharacterDecorator(c) { } 36 | 37 | string getAbilities() const override { 38 | return character->getAbilities() + " with HeightUp"; 39 | } 40 | 41 | }; 42 | 43 | // Concrete Decorator: Gun Shooting Power-Up. 44 | class GunPowerUp : public CharacterDecorator { 45 | public: 46 | GunPowerUp(Character* c) : CharacterDecorator(c) { } 47 | 48 | string getAbilities() const override { 49 | return character->getAbilities() + " with Gun"; 50 | } 51 | }; 52 | 53 | // Concrete Decorator: Star Power-Up (temporary ability). 54 | class StarPowerUp : public CharacterDecorator { 55 | public: 56 | StarPowerUp(Character* c) : CharacterDecorator(c) { } 57 | 58 | string getAbilities() const override { 59 | return character->getAbilities() + " with Star Power (Limited Time)"; 60 | } 61 | 62 | ~StarPowerUp() { 63 | cout << "Destroying StarPowerUp Decorator" << endl; 64 | } 65 | }; 66 | 67 | int main() { 68 | // Create a basic Mario character. 69 | Character* mario = new Mario(); 70 | cout << "Basic Character: " << mario->getAbilities() << endl; 71 | 72 | // Decorate Mario with a HeightUp power-up. 73 | mario = new HeightUp(mario); 74 | cout << "After HeightUp: " << mario->getAbilities() << endl; 75 | 76 | // Decorate Mario further with a GunPowerUp. 77 | mario = new GunPowerUp(mario); 78 | cout << "After GunPowerUp: " << mario->getAbilities() << endl; 79 | 80 | // Finally, add a StarPowerUp decoration. 81 | mario = new StarPowerUp(mario); 82 | cout << "After StarPowerUp: " << mario->getAbilities() << endl; 83 | 84 | delete mario; 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /Lecture 13/Java Code/DecoratorPattern.java: -------------------------------------------------------------------------------- 1 | // Component Interface: defines a common interface for Mario and all power-up decorators. 2 | interface Character { 3 | String getAbilities(); 4 | } 5 | 6 | // Concrete Component: Basic Mario character with no power-ups. 7 | class Mario implements Character { 8 | public String getAbilities() { 9 | return "Mario"; 10 | } 11 | } 12 | 13 | // Abstract Decorator: CharacterDecorator "is-a" Character and "has-a" Character. 14 | abstract class CharacterDecorator implements Character { 15 | protected Character character; // Wrapped component 16 | 17 | public CharacterDecorator(Character c) { 18 | this.character = c; 19 | } 20 | } 21 | 22 | // Concrete Decorator: Height-Increasing Power-Up. 23 | class HeightUp extends CharacterDecorator { 24 | public HeightUp(Character c) { 25 | super(c); 26 | } 27 | 28 | public String getAbilities() { 29 | return character.getAbilities() + " with HeightUp"; 30 | } 31 | } 32 | 33 | // Concrete Decorator: Gun Shooting Power-Up. 34 | class GunPowerUp extends CharacterDecorator { 35 | public GunPowerUp(Character c) { 36 | super(c); 37 | } 38 | 39 | public String getAbilities() { 40 | return character.getAbilities() + " with Gun"; 41 | } 42 | } 43 | 44 | // Concrete Decorator: Star Power-Up (temporary ability). 45 | class StarPowerUp extends CharacterDecorator { 46 | public StarPowerUp(Character c) { 47 | super(c); 48 | } 49 | 50 | public String getAbilities() { 51 | return character.getAbilities() + " with Star Power (Limited Time)"; 52 | } 53 | } 54 | 55 | public class DecoratorPattern { 56 | public static void main(String[] args) { 57 | // Create a basic Mario character. 58 | Character mario = new Mario(); 59 | System.out.println("Basic Character: " + mario.getAbilities()); 60 | 61 | // Decorate Mario with a HeightUp power-up. 62 | mario = new HeightUp(mario); 63 | System.out.println("After HeightUp: " + mario.getAbilities()); 64 | 65 | // Decorate Mario further with a GunPowerUp. 66 | mario = new GunPowerUp(mario); 67 | System.out.println("After GunPowerUp: " + mario.getAbilities()); 68 | 69 | // Finally, add a StarPowerUp decoration. 70 | mario = new StarPowerUp(mario); 71 | System.out.println("After StarPowerUp: " + mario.getAbilities()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Lecture 13/Standard UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 13/Standard UML.jpeg -------------------------------------------------------------------------------- /Lecture 14/UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 14/UML.jpeg -------------------------------------------------------------------------------- /Lecture 15/Example UML.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 15/Example UML.jpg -------------------------------------------------------------------------------- /Lecture 15/Standard UML.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 15/Standard UML.jpg -------------------------------------------------------------------------------- /Lecture 16/C++ Code/AdpaterPattern.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // 1. Target interface expected by the client 7 | class IReports { 8 | public: 9 | // now takes the raw data string and returns JSON 10 | virtual string getJsonData(const string& data) = 0; 11 | virtual ~IReports() {} 12 | }; 13 | 14 | // 2. Adaptee: provides XML data from a raw input 15 | class XmlDataProvider { 16 | public: 17 | // Expect data in "name:id" format (e.g. "Alice:42") 18 | string getXmlData(const string& data) { 19 | size_t sep = data.find(':'); 20 | string name = data.substr(0, sep); 21 | string id = data.substr(sep + 1); 22 | // Build an XML representation 23 | return "" 24 | "" + name + "" 25 | "" + id + "" 26 | ""; 27 | } 28 | }; 29 | 30 | // 3. Adapter: implements IReports by converting XML → JSON 31 | class XmlDataProviderAdapter : public IReports { 32 | private: 33 | XmlDataProvider* xmlProvider; 34 | public: 35 | XmlDataProviderAdapter(XmlDataProvider* provider) { 36 | this->xmlProvider = provider; 37 | } 38 | 39 | string getJsonData(const string& data) override { 40 | // 1. Get XML from the adaptee 41 | string xml = xmlProvider->getXmlData(data); 42 | 43 | // 2. Naïvely parse out and values 44 | size_t startName = xml.find("") + 6; 45 | size_t endName = xml.find(""); 46 | string name = xml.substr(startName, endName - startName); 47 | 48 | size_t startId = xml.find("") + 4; 49 | size_t endId = xml.find(""); 50 | string id = xml.substr(startId, endId - startId); 51 | 52 | // 3. Build and return JSON 53 | return "{\"name\":\"" + name + "\", \"id\":" + id + "}"; 54 | } 55 | }; 56 | 57 | // 4. Client code works only with IReports 58 | class Client { 59 | public: 60 | void getReport(IReports* report, string rawData) { 61 | cout << "Processed JSON: " 62 | << report->getJsonData(rawData) 63 | << endl; 64 | } 65 | }; 66 | 67 | int main() { 68 | // 1. Create the adaptee 69 | XmlDataProvider* xmlProv = new XmlDataProvider(); 70 | 71 | // 2. Make our adapter 72 | IReports* adapter = new XmlDataProviderAdapter(xmlProv); 73 | 74 | // 3. Give it some raw data 75 | string rawData = "Alice:42"; 76 | 77 | // 4. Client prints the JSON 78 | Client* client = new Client(); 79 | client->getReport(adapter, rawData); 80 | // → Processed JSON: {"name":"Alice", "id":42} 81 | 82 | // 5. Cleanup 83 | delete adapter; 84 | delete xmlProv; 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /Lecture 16/Example UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 16/Example UML.jpeg -------------------------------------------------------------------------------- /Lecture 16/Java Code/AdapterPattern.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | // 1. Target interface expected by the client 4 | interface IReports { 5 | // now takes the raw data string and returns JSON 6 | String getJsonData(String data); 7 | } 8 | 9 | // 2. Adaptee: provides XML data from a raw input 10 | class XmlDataProvider { 11 | // Expect data in "name:id" format (e.g. "Alice:42") 12 | String getXmlData(String data) { 13 | int sep = data.indexOf(':'); 14 | String name = data.substring(0, sep); 15 | String id = data.substring(sep + 1); 16 | // Build an XML representation 17 | return "" 18 | + "" + name + "" 19 | + "" + id + "" 20 | + ""; 21 | } 22 | } 23 | 24 | // 3. Adapter: implements IReports by converting XML → JSON 25 | class XmlDataProviderAdapter implements IReports { 26 | private XmlDataProvider xmlProvider; 27 | public XmlDataProviderAdapter(XmlDataProvider provider) { 28 | this.xmlProvider = provider; 29 | } 30 | 31 | public String getJsonData(String data) { 32 | // 1. Get XML from the adaptee 33 | String xml = xmlProvider.getXmlData(data); 34 | 35 | // 2. Naïvely parse out and values 36 | int startName = xml.indexOf("") + 6; 37 | int endName = xml.indexOf(""); 38 | String name = xml.substring(startName, endName); 39 | 40 | int startId = xml.indexOf("") + 4; 41 | int endId = xml.indexOf(""); 42 | String id = xml.substring(startId, endId); 43 | 44 | // 3. Build and return JSON 45 | return "{\"name\":\"" + name + "\", \"id\":" + id + "}"; 46 | } 47 | } 48 | 49 | // 4. Client code works only with IReports 50 | class Client { 51 | public void getReport(IReports report, String rawData) { 52 | System.out.println("Processed JSON: " 53 | + report.getJsonData(rawData)); 54 | } 55 | } 56 | 57 | public class AdapterPattern { 58 | public static void main(String[] args) { 59 | // 1. Create the adaptee 60 | XmlDataProvider xmlProv = new XmlDataProvider(); 61 | 62 | // 2. Make our adapter 63 | IReports adapter = new XmlDataProviderAdapter(xmlProv); 64 | 65 | // 3. Give it some raw data 66 | String rawData = "Alice:42"; 67 | 68 | // 4. Client prints the JSON 69 | Client client = new Client(); 70 | 71 | client.getReport(adapter, rawData); 72 | // → Processed JSON: {"name":"Alice", "id":42} 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Lecture 16/Standard UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 16/Standard UML.jpeg -------------------------------------------------------------------------------- /Lecture 17/C++ Code/FacadePattern.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // Subsystems 5 | class PowerSupply { 6 | public: 7 | void providePower() { 8 | cout << "Power Supply: Providing power..." << endl; 9 | } 10 | }; 11 | 12 | class CoolingSystem { 13 | public: 14 | void startFans() { 15 | cout << "Cooling System: Fans started..." << endl; 16 | } 17 | }; 18 | 19 | class CPU { 20 | public: 21 | void initialize() { 22 | cout << "CPU: Initialization started..." << endl; 23 | } 24 | }; 25 | 26 | class Memory { 27 | public: 28 | void selfTest() { 29 | cout << "Memory: Self-test passed..." << endl; 30 | } 31 | }; 32 | 33 | class HardDrive { 34 | public: 35 | void spinUp() { 36 | cout << "Hard Drive: Spinning up..." << endl; 37 | } 38 | }; 39 | 40 | class BIOS { 41 | public: 42 | void boot(CPU& cpu, Memory& memory) { 43 | cout << "BIOS: Booting CPU and Memory checks..." << endl; 44 | cpu.initialize(); 45 | memory.selfTest(); 46 | } 47 | }; 48 | 49 | class OperatingSystem { 50 | public: 51 | void load() { 52 | cout << "Operating System: Loading into memory..." << endl; 53 | } 54 | }; 55 | 56 | // Facade 57 | class ComputerFacade { 58 | private: 59 | PowerSupply powerSupply; 60 | CoolingSystem coolingSystem; 61 | CPU cpu; 62 | Memory memory; 63 | HardDrive hardDrive; 64 | BIOS bios; 65 | OperatingSystem os; 66 | 67 | public: 68 | void startComputer() { 69 | cout << "----- Starting Computer -----" << endl; 70 | powerSupply.providePower(); 71 | coolingSystem.startFans(); 72 | bios.boot(cpu, memory); 73 | hardDrive.spinUp(); 74 | os.load(); 75 | cout << "Computer Booted Successfully!" << endl; 76 | } 77 | }; 78 | 79 | // Client 80 | int main() { 81 | ComputerFacade computer; 82 | computer.startComputer(); 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /Lecture 17/Example UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 17/Example UML.jpeg -------------------------------------------------------------------------------- /Lecture 17/Java Code/FacadePattern.java: -------------------------------------------------------------------------------- 1 | 2 | // Subsystems 3 | class PowerSupply { 4 | public void providePower() { 5 | System.out.println("Power Supply: Providing power..."); 6 | } 7 | } 8 | 9 | class CoolingSystem { 10 | public void startFans() { 11 | System.out.println("Cooling System: Fans started..."); 12 | } 13 | } 14 | 15 | class CPU { 16 | public void initialize() { 17 | System.out.println("CPU: Initialization started..."); 18 | } 19 | } 20 | 21 | class Memory { 22 | public void selfTest() { 23 | System.out.println("Memory: Self-test passed..."); 24 | } 25 | } 26 | 27 | class HardDrive { 28 | public void spinUp() { 29 | System.out.println("Hard Drive: Spinning up..."); 30 | } 31 | } 32 | 33 | class BIOS { 34 | public void boot(CPU cpu, Memory memory) { 35 | System.out.println("BIOS: Booting CPU and Memory checks..."); 36 | cpu.initialize(); 37 | memory.selfTest(); 38 | } 39 | } 40 | 41 | class OperatingSystem { 42 | public void load() { 43 | System.out.println("Operating System: Loading into memory..."); 44 | } 45 | } 46 | 47 | // Facade 48 | class ComputerFacade { 49 | private PowerSupply powerSupply = new PowerSupply(); 50 | private CoolingSystem coolingSystem = new CoolingSystem(); 51 | private CPU cpu = new CPU(); 52 | private Memory memory = new Memory(); 53 | private HardDrive hardDrive = new HardDrive(); 54 | private BIOS bios = new BIOS(); 55 | private OperatingSystem os = new OperatingSystem(); 56 | 57 | public void startComputer() { 58 | System.out.println("----- Starting Computer -----"); 59 | powerSupply.providePower(); 60 | coolingSystem.startFans(); 61 | bios.boot(cpu, memory); 62 | hardDrive.spinUp(); 63 | os.load(); 64 | System.out.println("Computer Booted Successfully!"); 65 | } 66 | } 67 | 68 | // Client 69 | public class FacadePattern { 70 | public static void main(String[] args) { 71 | ComputerFacade computer = new ComputerFacade(); 72 | computer.startComputer(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Lecture 17/Standard UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 17/Standard UML.jpeg -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/MusicPlayerApplication.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "managers/PlaylistManager.hpp" 3 | #include "MusicPlayerFacade.hpp" 4 | 5 | using namespace std; 6 | 7 | class MusicPlayerApplication { 8 | private: 9 | static MusicPlayerApplication* instance; 10 | vector songLibrary; 11 | MusicPlayerApplication() {} 12 | 13 | public: 14 | static MusicPlayerApplication* getInstance() { 15 | if (!instance) { 16 | instance = new MusicPlayerApplication(); 17 | } 18 | return instance; 19 | } 20 | 21 | void createSongInLibrary(const string& title, const string& artist, 22 | const string& path) { 23 | Song* newSong = new Song(title, artist, path); 24 | songLibrary.push_back(newSong); 25 | } 26 | 27 | Song* findSongByTitle(const string& title) { 28 | for (Song* s : songLibrary) { 29 | if (s->getTitle() == title) { 30 | return s; 31 | } 32 | } 33 | return nullptr; 34 | } 35 | void createPlaylist(const string& playlistName) { 36 | PlaylistManager::getInstance()->createPlaylist(playlistName); 37 | } 38 | 39 | void addSongToPlaylist(const string& playlistName, 40 | const string& songTitle) { 41 | Song* song = findSongByTitle(songTitle); 42 | if (!song) { 43 | throw runtime_error("Song \"" + songTitle + "\" not found in library."); 44 | } 45 | PlaylistManager::getInstance() 46 | ->addSongToPlaylist(playlistName, song); 47 | } 48 | 49 | void connectAudioDevice(DeviceType deviceType) { 50 | MusicPlayerFacade::getInstance()->connectDevice(deviceType); 51 | } 52 | 53 | void selectPlayStrategy(PlayStrategyType strategyType) { 54 | MusicPlayerFacade::getInstance()->setPlayStrategy(strategyType); 55 | } 56 | 57 | void loadPlaylist(const string& playlistName) { 58 | MusicPlayerFacade::getInstance()->loadPlaylist(playlistName); 59 | } 60 | 61 | void playSingleSong(const string& songTitle) { 62 | Song* song = findSongByTitle(songTitle); 63 | if (!song) { 64 | throw runtime_error("Song \"" + songTitle + "\" not found."); 65 | } 66 | MusicPlayerFacade::getInstance()->playSong(song); 67 | } 68 | 69 | void pauseCurrentSong(const string& songTitle) { 70 | Song* song = findSongByTitle(songTitle); 71 | if (!song) { 72 | throw runtime_error("Song \"" + songTitle + "\" not found."); 73 | } 74 | MusicPlayerFacade::getInstance()->pauseSong(song); 75 | } 76 | 77 | void playAllTracksInPlaylist() { 78 | MusicPlayerFacade::getInstance()->playAllTracks(); 79 | } 80 | 81 | void playPreviousTrackInPlaylist() { 82 | MusicPlayerFacade::getInstance()->playPreviousTrack(); 83 | } 84 | 85 | void queueSongNext(const string& songTitle) { 86 | Song* song = findSongByTitle(songTitle); 87 | if (!song) { 88 | throw runtime_error("Song \"" + songTitle + "\" not found."); 89 | } 90 | MusicPlayerFacade::getInstance()->enqueueNext(song); 91 | } 92 | }; 93 | 94 | MusicPlayerApplication* MusicPlayerApplication::instance = nullptr; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/core/AudioEngine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../models/Song.hpp" 3 | #include "../device/IAudioOutputDevice.hpp" 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | class AudioEngine { 10 | private: 11 | Song* currentSong; 12 | bool songIsPaused; 13 | public: 14 | AudioEngine() { 15 | currentSong = nullptr; 16 | songIsPaused = false; 17 | } 18 | string getCurrentSongTitle() const { 19 | if (currentSong) { 20 | return currentSong->getTitle(); 21 | } 22 | return ""; 23 | } 24 | bool isPaused() const { 25 | return songIsPaused; 26 | } 27 | void play(IAudioOutputDevice* aod, Song* song) { 28 | if (song == nullptr) { 29 | throw runtime_error("Cannot play a null song."); 30 | } 31 | // Resume if same song was paused 32 | if (songIsPaused && song == currentSong) { 33 | songIsPaused = false; 34 | cout << "Resuming song: " << song->getTitle() << "\n"; 35 | aod->playAudio(song); 36 | return; 37 | } 38 | 39 | currentSong = song; 40 | songIsPaused = false; 41 | cout << "Playing song: " << song->getTitle() << "\n"; 42 | aod->playAudio(song); 43 | } 44 | 45 | void pause() { 46 | if (currentSong == nullptr) { 47 | throw runtime_error("No song is currently playing to pause."); 48 | } 49 | if (songIsPaused) { 50 | throw runtime_error("Song is already paused."); 51 | } 52 | songIsPaused = true; 53 | cout << "Pausing song: " << currentSong->getTitle() << "\n"; 54 | } 55 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/device/BluetoothSpeakerAdapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../models/Song.hpp" 3 | #include "IAudioOutputDevice.hpp" 4 | #include "../external/BluetoothSpeakerAPI.hpp" 5 | 6 | using namespace std; 7 | 8 | class BluetoothSpeakerAdapter : public IAudioOutputDevice { 9 | private: 10 | BluetoothSpeakerAPI* bluetoothApi; 11 | public: 12 | BluetoothSpeakerAdapter(BluetoothSpeakerAPI* api) { 13 | bluetoothApi = api; 14 | } 15 | 16 | void playAudio(Song* song) override { 17 | string payload = song->getTitle() + " by " + song->getArtist(); 18 | bluetoothApi->playSoundViaBluetooth(payload); 19 | } 20 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/device/HeadphonesAdapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../models/Song.hpp" 3 | #include "IAudioOutputDevice.hpp" 4 | #include "../external/HeadphonesAPI.hpp" 5 | 6 | using namespace std; 7 | 8 | class HeadphonesAdapter : public IAudioOutputDevice { 9 | private: 10 | HeadphonesAPI* headphonesApi; 11 | public: 12 | HeadphonesAdapter(HeadphonesAPI* api) { 13 | headphonesApi = api; 14 | } 15 | 16 | void playAudio(Song* song) override { 17 | string payload = song->getTitle() + " by " + song->getArtist(); 18 | headphonesApi->playSoundViaJack(payload); 19 | } 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/device/IAudioOutputDevice.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../models/Song.hpp" 3 | 4 | class IAudioOutputDevice { 5 | public: 6 | virtual ~IAudioOutputDevice() {} 7 | virtual void playAudio(Song* song) = 0; 8 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/device/WiredSpeakerAdapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../models/Song.hpp" 3 | #include "IAudioOutputDevice.hpp" 4 | #include "../external/WiredSpeakerAPI.hpp" 5 | 6 | using namespace std; 7 | 8 | class WiredSpeakerAdapter : public IAudioOutputDevice { 9 | private: 10 | WiredSpeakerAPI* wiredApi; 11 | public: 12 | WiredSpeakerAdapter(WiredSpeakerAPI* api) { 13 | wiredApi = api; 14 | } 15 | 16 | void playAudio(Song* song) override { 17 | string payload = song->getTitle() + " by " + song->getArtist(); 18 | wiredApi->playSoundViaCable(payload); 19 | } 20 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/enums/DeviceType.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class DeviceType { 4 | BLUETOOTH, 5 | WIRED, 6 | HEADPHONES 7 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/enums/PlayStrategyType.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class PlayStrategyType { 4 | SEQUENTIAL, 5 | RANDOM, 6 | CUSTOM_QUEUE 7 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/external/BluetoothSpeakerAPI.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | class BluetoothSpeakerAPI { 8 | public: 9 | void playSoundViaBluetooth(const string& data) { 10 | cout << "[BluetoothSpeaker] Playing: " << data << "\n"; 11 | // mimics playing music 12 | } 13 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/external/HeadphonesAPI.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | class HeadphonesAPI { 8 | public: 9 | void playSoundViaJack(const string& data) { 10 | cout << "[Headphones] Playing: " << data << "\n"; 11 | // mimics playing music 12 | } 13 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/external/WiredSpeakerAPI.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | class WiredSpeakerAPI { 8 | public: 9 | void playSoundViaCable(const string& data) { 10 | cout << "[WiredSpeaker] Playing: " << data << "\n"; 11 | // mimics playing music 12 | } 13 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/factories/DeviceFactory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../device/IAudioOutputDevice.hpp" 4 | #include "../device/BluetoothSpeakerAdapter.hpp" 5 | #include "../device/WiredSpeakerAdapter.hpp" 6 | #include "../device/HeadphonesAdapter.hpp" 7 | #include "../enums/DeviceType.hpp" 8 | 9 | using namespace std; 10 | 11 | class DeviceFactory { 12 | public: 13 | static IAudioOutputDevice* createDevice(DeviceType deviceType) { 14 | if (deviceType == DeviceType::BLUETOOTH) { 15 | return new BluetoothSpeakerAdapter(new BluetoothSpeakerAPI()); 16 | } else if (deviceType == DeviceType::WIRED) { 17 | return new WiredSpeakerAdapter(new WiredSpeakerAPI()); 18 | } else { // HEADPHONES 19 | return new HeadphonesAdapter(new HeadphonesAPI()); 20 | } 21 | } 22 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/folderstructure.txt: -------------------------------------------------------------------------------- 1 | MusicPlayerApp/ 2 | │ 3 | ├── main.cpp # Composition root and entry point 4 | ├── MusicPlayerFacade.hpp # Facade class (orchestrator) 5 | ├── MusicPlayerApplication.hpp # High-level application/demo runner 6 | │ 7 | ├── core/ 8 | │ └── AudioEngine.hpp # Playback engine 9 | │ 10 | ├── enums/ # All shared enum types 11 | │ ├── DeviceType.hpp # enum class DeviceType { BLUETOOTH, WIRED, HEADPHONES } 12 | │ └── PlayStrategyType.hpp # enum class PlayStrategyType { SEQUENTIAL, RANDOM, CUSTOM_QUEUE } 13 | │ 14 | ├── models/ 15 | │ ├── Song.hpp 16 | │ └── Playlist.hpp 17 | │ 18 | ├── managers/ 19 | │ ├── PlaylistManager.hpp 20 | │ ├── DeviceManager.hpp 21 | | └── StrategyManager.hpp 22 | │ 23 | ├── strategies/ 24 | │ ├── PlayStrategy.hpp 25 | │ ├── SequentialPlayStrategy.hpp 26 | │ ├── RandomPlayStrategy.hpp 27 | │ └── CustomQueueStrategy.hpp 28 | │ 29 | ├── device/ # Audio device interfaces & adapters 30 | │ ├── IAudioOutputDevice.hpp 31 | │ ├── BluetoothSpeakerAdapter.hpp 32 | │ ├── WiredSpeakerAdapter.hpp 33 | │ └── HeadphonesAdapter.hpp 34 | | 35 | ├── external/ # External devices 36 | │ ├── BluetoothSpeakerAPI.hpp 37 | │ ├── HeadphonesAPI.hpp 38 | │ └── WiredSpeakerAPI.hpp 39 | │ 40 | └── factories/ 41 | └── DeviceFactory.hpp # Creates IAudioOutputDevice instances 42 | -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MusicPlayerApplication.hpp" 2 | 3 | using namespace std; 4 | 5 | int main() { 6 | try { 7 | auto application = MusicPlayerApplication::getInstance(); 8 | 9 | // Populate library 10 | application->createSongInLibrary("Kesariya", "Arijit Singh", "/music/kesariya.mp3"); 11 | application->createSongInLibrary("Chaiyya Chaiyya", "Sukhwinder Singh", "/music/chaiyya_chaiyya.mp3"); 12 | application->createSongInLibrary("Tum Hi Ho", "Arijit Singh", "/music/tum_hi_ho.mp3"); 13 | application->createSongInLibrary("Jai Ho", "A. R. Rahman", "/music/jai_ho.mp3"); 14 | application->createSongInLibrary("Zinda", "Siddharth Mahadevan", "/music/zinda.mp3"); 15 | 16 | // Create playlist and add songs 17 | application->createPlaylist("Bollywood Vibes"); 18 | application->addSongToPlaylist("Bollywood Vibes", "Kesariya"); 19 | application->addSongToPlaylist("Bollywood Vibes", "Chaiyya Chaiyya"); 20 | application->addSongToPlaylist("Bollywood Vibes", "Tum Hi Ho"); 21 | application->addSongToPlaylist("Bollywood Vibes", "Jai Ho"); 22 | 23 | // Connect device 24 | application->connectAudioDevice(DeviceType::BLUETOOTH); 25 | 26 | //Play/pause a single song 27 | application->playSingleSong("Zinda"); 28 | application->pauseCurrentSong("Zinda"); 29 | application->playSingleSong("Zinda"); // resume 30 | 31 | cout << "\n-- Sequential Playback --\n"; 32 | application->selectPlayStrategy(PlayStrategyType::SEQUENTIAL); 33 | application->loadPlaylist("Bollywood Vibes"); 34 | application->playAllTracksInPlaylist(); 35 | 36 | cout << "\n-- Random Playback --\n"; 37 | application->selectPlayStrategy(PlayStrategyType::RANDOM); 38 | application->loadPlaylist("Bollywood Vibes"); 39 | application->playAllTracksInPlaylist(); 40 | 41 | cout << "\n-- Custom Queue Playback --\n"; 42 | application->selectPlayStrategy(PlayStrategyType::CUSTOM_QUEUE); 43 | application->loadPlaylist("Bollywood Vibes"); 44 | application->queueSongNext("Kesariya"); 45 | application->queueSongNext("Tum Hi Ho"); 46 | application->playAllTracksInPlaylist(); 47 | 48 | cout << "\n-- Play Previous in Sequential --\n"; 49 | application->selectPlayStrategy(PlayStrategyType::SEQUENTIAL); 50 | application->loadPlaylist("Bollywood Vibes"); 51 | application->playAllTracksInPlaylist(); 52 | 53 | application->playPreviousTrackInPlaylist(); 54 | application->playPreviousTrackInPlaylist(); 55 | 56 | } catch (const exception& error) { 57 | cerr << "Error: " << error.what() << endl; 58 | } 59 | return 0; 60 | } -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/managers/DeviceManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../device/IAudioOutputDevice.hpp" 4 | #include "../enums/DeviceType.hpp" 5 | #include "../factories/DeviceFactory.hpp" 6 | 7 | using namespace std; 8 | 9 | class DeviceManager { 10 | private: 11 | static DeviceManager* instance; 12 | IAudioOutputDevice* currentOutputDevice; 13 | DeviceManager() { 14 | currentOutputDevice = nullptr; 15 | } 16 | public: 17 | static DeviceManager* getInstance() { 18 | if (instance == nullptr) { 19 | instance = new DeviceManager(); 20 | } 21 | return instance; 22 | } 23 | void connect(DeviceType deviceType) { 24 | if (currentOutputDevice) { 25 | delete currentOutputDevice; 26 | } 27 | 28 | currentOutputDevice = DeviceFactory::createDevice(deviceType); 29 | 30 | switch(deviceType) { 31 | case DeviceType::BLUETOOTH: 32 | cout<< "Bluetooth device connected \n"; 33 | break; 34 | case DeviceType::WIRED: 35 | cout<< "Wired device connected \n"; 36 | break; 37 | case DeviceType::HEADPHONES: 38 | cout<< "Headphones connected \n"; 39 | } 40 | } 41 | 42 | IAudioOutputDevice* getOutputDevice() { 43 | if (!currentOutputDevice) { 44 | throw runtime_error("No output device is connected."); 45 | } 46 | return currentOutputDevice; 47 | } 48 | 49 | bool hasOutputDevice() { 50 | return currentOutputDevice != nullptr; 51 | } 52 | }; 53 | 54 | DeviceManager* DeviceManager::instance = nullptr; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/managers/PlaylistManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../models/Playlist.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | class PlaylistManager { 10 | private: 11 | static PlaylistManager* instance; 12 | map playlists; 13 | PlaylistManager() {} 14 | public: 15 | static PlaylistManager* getInstance() { 16 | if (!instance) { 17 | instance = new PlaylistManager(); 18 | } 19 | return instance; 20 | } 21 | 22 | void createPlaylist(const string& name) { 23 | if (playlists.count(name)) { 24 | throw runtime_error("Playlist \"" + name + "\" already exists."); 25 | } 26 | playlists[name] = new Playlist(name); 27 | } 28 | 29 | void addSongToPlaylist(const string& playlistName, Song* song) { 30 | if (!playlists.count(playlistName)) { 31 | throw runtime_error("Playlist \"" + playlistName + "\" not found."); 32 | } 33 | playlists[playlistName]->addSongToPlaylist(song); 34 | } 35 | 36 | Playlist* getPlaylist(const string& name) { 37 | if (!playlists.count(name)) { 38 | throw runtime_error("Playlist \"" + name + "\" not found."); 39 | } 40 | return playlists[name]; 41 | } 42 | }; 43 | PlaylistManager* PlaylistManager::instance = nullptr; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/managers/StrategyManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../strategies/SequentialPlayStrategy.hpp" 4 | #include "../strategies/CustomQueueStrategy.hpp" 5 | #include "../strategies/RandomPlayStrategy.hpp" 6 | #include "../enums/PlayStrategyType.hpp" 7 | 8 | using namespace std; 9 | 10 | class StrategyManager { 11 | private: 12 | static StrategyManager* instance; 13 | SequentialPlayStrategy* sequentialStrategy; 14 | RandomPlayStrategy* randomStrategy; 15 | CustomQueueStrategy* customQueueStrategy; 16 | 17 | StrategyManager() { 18 | sequentialStrategy = new SequentialPlayStrategy(); 19 | randomStrategy = new RandomPlayStrategy(); 20 | customQueueStrategy = new CustomQueueStrategy(); 21 | } 22 | public: 23 | static StrategyManager* getInstance() { 24 | if (!instance) { 25 | instance = new StrategyManager(); 26 | } 27 | return instance; 28 | } 29 | PlayStrategy* getStrategy(PlayStrategyType type) { 30 | if (type == PlayStrategyType::SEQUENTIAL) { 31 | return sequentialStrategy; 32 | } else if (type == PlayStrategyType::RANDOM) { 33 | return randomStrategy; 34 | } else { 35 | return customQueueStrategy; 36 | } 37 | } 38 | }; 39 | 40 | StrategyManager* StrategyManager::instance = nullptr; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/models/Playlist.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "Song.hpp" 6 | 7 | using namespace std; 8 | 9 | class Playlist { 10 | private: 11 | string playlistName; 12 | vector songList; 13 | public: 14 | Playlist(string name) { 15 | playlistName = name; 16 | } 17 | string getPlaylistName() { 18 | return playlistName; 19 | } 20 | const vector getSongs() { 21 | return songList; 22 | } 23 | int getSize() { 24 | return (int)songList.size(); 25 | } 26 | void addSongToPlaylist(Song* song) { 27 | if (song == nullptr) { 28 | throw runtime_error("Cannot add null song to playlist."); 29 | } 30 | songList.push_back(song); 31 | } 32 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/models/Song.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | class Song { 8 | private: 9 | string title; 10 | string artist; 11 | string filePath; 12 | public: 13 | Song(string t, string a, string f) { 14 | title = t; 15 | artist = a; 16 | filePath = f; 17 | } 18 | string getTitle() { 19 | return title; 20 | } 21 | string getArtist() { 22 | return artist; 23 | } 24 | string getFilePath() { 25 | return filePath; 26 | } 27 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/strategies/CustomQueueStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../models/Playlist.hpp" 4 | #include "PlayStrategy.hpp" 5 | 6 | class CustomQueueStrategy : public PlayStrategy { 7 | private: 8 | Playlist* currentPlaylist; 9 | int currentIndex; 10 | queue nextQueue; 11 | stack prevStack; 12 | 13 | Song* nextSequential() { 14 | if (currentPlaylist->getSize() == 0) { 15 | throw runtime_error("Playlist is empty."); 16 | } 17 | currentIndex = currentIndex + 1; 18 | return currentPlaylist->getSongs()[currentIndex]; 19 | } 20 | 21 | Song* previousSequential() { 22 | if (currentPlaylist->getSize() == 0) { 23 | throw runtime_error("Playlist is empty."); 24 | } 25 | currentIndex = currentIndex - 1; 26 | return currentPlaylist->getSongs()[currentIndex]; 27 | } 28 | 29 | public: 30 | CustomQueueStrategy() { 31 | currentPlaylist = nullptr; 32 | currentIndex = -1; 33 | } 34 | 35 | void setPlaylist(Playlist* playlist) override { 36 | currentPlaylist = playlist; 37 | currentIndex = -1; 38 | while (!nextQueue.empty()) { 39 | nextQueue.pop(); 40 | } 41 | while(!prevStack.empty()) { 42 | prevStack.pop(); 43 | } 44 | } 45 | 46 | bool hasNext() override { 47 | return ((currentIndex + 1) < currentPlaylist->getSize()); 48 | } 49 | 50 | Song* next() override { 51 | if (!currentPlaylist || currentPlaylist->getSize() == 0) { 52 | throw runtime_error("No playlist loaded or playlist is empty."); 53 | } 54 | 55 | if (!nextQueue.empty()) { 56 | Song* s = nextQueue.front(); 57 | nextQueue.pop(); 58 | prevStack.push(s); 59 | 60 | // update index to match queued song 61 | auto& list = currentPlaylist->getSongs(); 62 | for (int i = 0; i < (int)list.size(); ++i) { 63 | if (list[i] == s) { 64 | currentIndex = i; 65 | break; 66 | } 67 | } 68 | return s; 69 | } 70 | 71 | // Otherwise sequential 72 | return nextSequential(); 73 | } 74 | 75 | bool hasPrevious() override { 76 | return (currentIndex - 1 > 0); 77 | } 78 | 79 | Song* previous() override { 80 | if (!currentPlaylist || currentPlaylist->getSize() == 0) { 81 | throw runtime_error("No playlist loaded or playlist is empty."); 82 | } 83 | 84 | if (!prevStack.empty()) { 85 | Song* s = prevStack.top(); 86 | prevStack.pop(); 87 | 88 | // update index to match stacked song 89 | auto& list = currentPlaylist->getSongs(); 90 | for (int i = 0; i < (int)list.size(); ++i) { 91 | if (list[i] == s) { 92 | currentIndex = i; 93 | break; 94 | } 95 | } 96 | return s; 97 | } 98 | 99 | // Otherwise sequential 100 | return previousSequential(); 101 | } 102 | 103 | void addToNext(Song* song) override { 104 | if (!song) { 105 | throw runtime_error("Cannot enqueue null song."); 106 | } 107 | nextQueue.push(song); 108 | } 109 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/strategies/PlayStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../models/Song.hpp" 4 | #include "../models/Playlist.hpp" 5 | 6 | using namespace std; 7 | 8 | class PlayStrategy { 9 | public: 10 | virtual ~PlayStrategy() {} 11 | virtual void setPlaylist(Playlist* playlist) = 0; 12 | virtual Song* next() = 0; 13 | virtual bool hasNext() = 0; 14 | virtual Song* previous() = 0; 15 | virtual bool hasPrevious() = 0; 16 | virtual void addToNext(Song* song) {} 17 | }; -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/strategies/RandomPlayStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../models/Playlist.hpp" 4 | #include "PlayStrategy.hpp" 5 | 6 | using namespace std; 7 | 8 | class RandomPlayStrategy : public PlayStrategy { 9 | private: 10 | Playlist* currentPlaylist; 11 | vector remainingSongs; 12 | stack history; 13 | 14 | public: 15 | RandomPlayStrategy() { 16 | currentPlaylist = nullptr; 17 | srand((unsigned)time(nullptr)); 18 | } 19 | 20 | void setPlaylist(Playlist* playlist) override { 21 | currentPlaylist = playlist; 22 | if (!currentPlaylist || currentPlaylist->getSize() == 0) return; 23 | 24 | remainingSongs = currentPlaylist->getSongs(); 25 | history = stack(); 26 | } 27 | 28 | bool hasNext() override { 29 | return currentPlaylist && !remainingSongs.empty(); 30 | } 31 | 32 | // Next in Loop 33 | Song* next() override { 34 | if (!currentPlaylist || currentPlaylist->getSize() == 0) { 35 | throw runtime_error("No playlist loaded or playlist is empty."); 36 | } 37 | if (remainingSongs.empty()) { 38 | throw runtime_error("No songs left to play"); 39 | } 40 | 41 | int idx = rand() % remainingSongs.size(); 42 | Song* selectedSong = remainingSongs[idx]; 43 | 44 | // Remove the selectedSong from the list. (Swap and pop to remove in O(1)) 45 | swap(remainingSongs[idx], remainingSongs.back()); 46 | remainingSongs.pop_back(); 47 | 48 | history.push(selectedSong); 49 | return selectedSong; 50 | } 51 | 52 | bool hasPrevious() override { 53 | return history.size() > 0; 54 | } 55 | 56 | Song* previous() override { 57 | if (history.empty()) { 58 | throw std::runtime_error("No previous song available."); 59 | } 60 | 61 | Song* song = history.top(); 62 | history.pop(); 63 | return song; 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /Lecture 18/C++ Code/MusicPlayerSystem/MusicPlayerApplication/strategies/SequentialPlayStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../models/Playlist.hpp" 4 | #include "PlayStrategy.hpp" 5 | 6 | using namespace std; 7 | 8 | class SequentialPlayStrategy : public PlayStrategy { 9 | private: 10 | Playlist* currentPlaylist; 11 | int currentIndex; 12 | public: 13 | SequentialPlayStrategy() { 14 | currentPlaylist = nullptr; 15 | currentIndex = -1; 16 | } 17 | 18 | void setPlaylist(Playlist* playlist) override { 19 | currentPlaylist = playlist; 20 | currentIndex = -1; 21 | } 22 | 23 | bool hasNext() override { 24 | return ((currentIndex + 1) < currentPlaylist->getSize()); 25 | } 26 | 27 | // Next in Loop 28 | Song* next() override { 29 | if (!currentPlaylist || currentPlaylist->getSize() == 0) { 30 | throw runtime_error("No playlist loaded or playlist is empty."); 31 | } 32 | currentIndex = currentIndex + 1; 33 | return currentPlaylist->getSongs()[currentIndex]; 34 | } 35 | 36 | bool hasPrevious() override { 37 | return (currentIndex - 1 > 0); 38 | } 39 | 40 | // previous in Loop 41 | Song* previous() override { 42 | if (!currentPlaylist || currentPlaylist->getSize() == 0) { 43 | throw runtime_error("No playlist loaded or playlist is empty."); 44 | } 45 | currentIndex = currentIndex - 1; 46 | return currentPlaylist->getSongs()[currentIndex]; 47 | } 48 | }; -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/Main.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication; 2 | 3 | import MusicPlayerApplication.enums.DeviceType; 4 | import MusicPlayerApplication.enums.PlayStrategyType; 5 | 6 | public class Main { 7 | public static void main(String[] args) { 8 | try { 9 | MusicPlayerApplication application = MusicPlayerApplication.getInstance(); 10 | 11 | // Populate library 12 | application.createSongInLibrary("Kesariya", "Arijit Singh", "/music/kesariya.mp3"); 13 | application.createSongInLibrary("Chaiyya Chaiyya", "Sukhwinder Singh", "/music/chaiyya_chaiyya.mp3"); 14 | application.createSongInLibrary("Tum Hi Ho", "Arijit Singh", "/music/tum_hi_ho.mp3"); 15 | application.createSongInLibrary("Jai Ho", "A. R. Rahman", "/music/jai_ho.mp3"); 16 | application.createSongInLibrary("Zinda", "Siddharth Mahadevan", "/music/zinda.mp3"); 17 | 18 | // Create playlist and add songs 19 | application.createPlaylist("Bollywood Vibes"); 20 | application.addSongToPlaylist("Bollywood Vibes", "Kesariya"); 21 | application.addSongToPlaylist("Bollywood Vibes", "Chaiyya Chaiyya"); 22 | application.addSongToPlaylist("Bollywood Vibes", "Tum Hi Ho"); 23 | application.addSongToPlaylist("Bollywood Vibes", "Jai Ho"); 24 | 25 | // Connect device 26 | application.connectAudioDevice(DeviceType.BLUETOOTH); 27 | 28 | //Play/pause a single song 29 | application.playSingleSong("Zinda"); 30 | application.pauseCurrentSong("Zinda"); 31 | application.playSingleSong("Zinda"); // resume 32 | 33 | System.out.println("\n-- Sequential Playback --\n"); 34 | application.selectPlayStrategy(PlayStrategyType.SEQUENTIAL); 35 | application.loadPlaylist("Bollywood Vibes"); 36 | application.playAllTracksInPlaylist(); 37 | 38 | System.out.println("\n-- Random Playback --\n"); 39 | application.selectPlayStrategy(PlayStrategyType.RANDOM); 40 | application.loadPlaylist("Bollywood Vibes"); 41 | application.playAllTracksInPlaylist(); 42 | 43 | System.out.println("\n-- Custom Queue Playback --\n"); 44 | application.selectPlayStrategy(PlayStrategyType.CUSTOM_QUEUE); 45 | application.loadPlaylist("Bollywood Vibes"); 46 | application.queueSongNext("Kesariya"); 47 | application.queueSongNext("Tum Hi Ho"); 48 | application.playAllTracksInPlaylist(); 49 | 50 | System.out.println("\n-- Play Previous in Sequential --\n"); 51 | application.selectPlayStrategy(PlayStrategyType.SEQUENTIAL); 52 | application.loadPlaylist("Bollywood Vibes"); 53 | application.playAllTracksInPlaylist(); 54 | 55 | application.playPreviousTrackInPlaylist(); 56 | application.playPreviousTrackInPlaylist(); 57 | 58 | } catch (Exception error) { 59 | System.err.println("Error: " + error.getMessage()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/MusicPlayerApplication.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication; 2 | 3 | import MusicPlayerApplication.models.Song; 4 | import MusicPlayerApplication.managers.PlaylistManager; 5 | import MusicPlayerApplication.enums.DeviceType; 6 | import MusicPlayerApplication.enums.PlayStrategyType; 7 | 8 | public class MusicPlayerApplication { 9 | private static MusicPlayerApplication instance = null; 10 | private java.util.List songLibrary; 11 | 12 | private MusicPlayerApplication() { 13 | songLibrary = new java.util.ArrayList<>(); 14 | } 15 | 16 | public static synchronized MusicPlayerApplication getInstance() { 17 | if (instance == null) { 18 | instance = new MusicPlayerApplication(); 19 | } 20 | return instance; 21 | } 22 | 23 | public void createSongInLibrary(String title, String artist, String path) { 24 | Song newSong = new Song(title, artist, path); 25 | songLibrary.add(newSong); 26 | } 27 | 28 | public Song findSongByTitle(String title) { 29 | for (Song s : songLibrary) { 30 | if (s.getTitle().equals(title)) { 31 | return s; 32 | } 33 | } 34 | return null; 35 | } 36 | 37 | public void createPlaylist(String playlistName) { 38 | PlaylistManager.getInstance().createPlaylist(playlistName); 39 | } 40 | 41 | public void addSongToPlaylist(String playlistName, String songTitle) { 42 | Song song = findSongByTitle(songTitle); 43 | if (song == null) { 44 | throw new RuntimeException("Song \"" + songTitle + "\" not found in library."); 45 | } 46 | PlaylistManager.getInstance().addSongToPlaylist(playlistName, song); 47 | } 48 | 49 | public void connectAudioDevice(DeviceType deviceType) { 50 | MusicPlayerFacade.getInstance().connectDevice(deviceType); 51 | } 52 | 53 | public void selectPlayStrategy(PlayStrategyType strategyType) { 54 | MusicPlayerFacade.getInstance().setPlayStrategy(strategyType); 55 | } 56 | 57 | public void loadPlaylist(String playlistName) { 58 | MusicPlayerFacade.getInstance().loadPlaylist(playlistName); 59 | } 60 | 61 | public void playSingleSong(String songTitle) { 62 | Song song = findSongByTitle(songTitle); 63 | if (song == null) { 64 | throw new RuntimeException("Song \"" + songTitle + "\" not found."); 65 | } 66 | MusicPlayerFacade.getInstance().playSong(song); 67 | } 68 | 69 | public void pauseCurrentSong(String songTitle) { 70 | Song song = findSongByTitle(songTitle); 71 | if (song == null) { 72 | throw new RuntimeException("Song \"" + songTitle + "\" not found."); 73 | } 74 | MusicPlayerFacade.getInstance().pauseSong(song); 75 | } 76 | 77 | public void playAllTracksInPlaylist() { 78 | MusicPlayerFacade.getInstance().playAllTracks(); 79 | } 80 | 81 | public void playPreviousTrackInPlaylist() { 82 | MusicPlayerFacade.getInstance().playPreviousTrack(); 83 | } 84 | 85 | public void queueSongNext(String songTitle) { 86 | Song song = findSongByTitle(songTitle); 87 | if (song == null) { 88 | throw new RuntimeException("Song \"" + songTitle + "\" not found."); 89 | } 90 | MusicPlayerFacade.getInstance().enqueueNext(song); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/core/AudioEngine.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.core; 2 | 3 | import MusicPlayerApplication.models.Song; 4 | import MusicPlayerApplication.device.IAudioOutputDevice; 5 | 6 | public class AudioEngine { 7 | private Song currentSong; 8 | private boolean songIsPaused; 9 | 10 | public AudioEngine() { 11 | currentSong = null; 12 | songIsPaused = false; 13 | } 14 | 15 | public String getCurrentSongTitle() { 16 | if (currentSong != null) { 17 | return currentSong.getTitle(); 18 | } 19 | return ""; 20 | } 21 | 22 | public boolean isPaused() { 23 | return songIsPaused; 24 | } 25 | 26 | public void play(IAudioOutputDevice aod, Song song) { 27 | if (song == null) { 28 | throw new RuntimeException("Cannot play a null song."); 29 | } 30 | // Resume if same song was paused 31 | if (songIsPaused && song == currentSong) { 32 | songIsPaused = false; 33 | System.out.println("Resuming song: " + song.getTitle()); 34 | aod.playAudio(song); 35 | return; 36 | } 37 | 38 | currentSong = song; 39 | songIsPaused = false; 40 | System.out.println("Playing song: " + song.getTitle()); 41 | aod.playAudio(song); 42 | } 43 | 44 | public void pause() { 45 | if (currentSong == null) { 46 | throw new RuntimeException("No song is currently playing to pause."); 47 | } 48 | if (songIsPaused) { 49 | throw new RuntimeException("Song is already paused."); 50 | } 51 | songIsPaused = true; 52 | System.out.println("Pausing song: " + currentSong.getTitle()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/device/BluetoothSpeakerAdapter.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.device; 2 | 3 | import MusicPlayerApplication.models.Song; 4 | import MusicPlayerApplication.external.BluetoothSpeakerAPI; 5 | 6 | public class BluetoothSpeakerAdapter implements IAudioOutputDevice { 7 | private BluetoothSpeakerAPI bluetoothApi; 8 | 9 | public BluetoothSpeakerAdapter(BluetoothSpeakerAPI api) { 10 | this.bluetoothApi = api; 11 | } 12 | 13 | @Override 14 | public void playAudio(Song song) { 15 | String payload = song.getTitle() + " by " + song.getArtist(); 16 | bluetoothApi.playSoundViaBluetooth(payload); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/device/HeadphonesAdapter.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.device; 2 | 3 | import MusicPlayerApplication.models.Song; 4 | import MusicPlayerApplication.external.HeadphonesAPI; 5 | 6 | public class HeadphonesAdapter implements IAudioOutputDevice { 7 | private HeadphonesAPI headphonesApi; 8 | 9 | public HeadphonesAdapter(HeadphonesAPI api) { 10 | this.headphonesApi = api; 11 | } 12 | 13 | @Override 14 | public void playAudio(Song song) { 15 | String payload = song.getTitle() + " by " + song.getArtist(); 16 | headphonesApi.playSoundViaJack(payload); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/device/IAudioOutputDevice.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.device; 2 | 3 | import MusicPlayerApplication.models.Song; 4 | 5 | public interface IAudioOutputDevice { 6 | void playAudio(Song song); 7 | } 8 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/device/WiredSpeakerAdapter.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.device; 2 | 3 | import MusicPlayerApplication.models.Song; 4 | import MusicPlayerApplication.external.WiredSpeakerAPI; 5 | 6 | public class WiredSpeakerAdapter implements IAudioOutputDevice { 7 | private WiredSpeakerAPI wiredApi; 8 | 9 | public WiredSpeakerAdapter(WiredSpeakerAPI api) { 10 | this.wiredApi = api; 11 | } 12 | 13 | @Override 14 | public void playAudio(Song song) { 15 | String payload = song.getTitle() + " by " + song.getArtist(); 16 | wiredApi.playSoundViaCable(payload); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/enums/DeviceType.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.enums; 2 | 3 | 4 | public enum DeviceType { 5 | BLUETOOTH, 6 | WIRED, 7 | HEADPHONES 8 | } 9 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/enums/PlayStrategyType.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.enums; 2 | 3 | 4 | public enum PlayStrategyType { 5 | SEQUENTIAL, 6 | RANDOM, 7 | CUSTOM_QUEUE 8 | } 9 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/external/BluetoothSpeakerAPI.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.external; 2 | 3 | public class BluetoothSpeakerAPI { 4 | public void playSoundViaBluetooth(String data) { 5 | System.out.println("[BluetoothSpeaker] Playing: " + data); 6 | // mimics playing music 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/external/HeadphonesAPI.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.external; 2 | 3 | public class HeadphonesAPI { 4 | public void playSoundViaJack(String data) { 5 | System.out.println("[Headphones] Playing: " + data); 6 | // mimics playing music 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/external/WiredSpeakerAPI.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.external; 2 | 3 | 4 | public class WiredSpeakerAPI { 5 | public void playSoundViaCable(String data) { 6 | System.out.println("[WiredSpeaker] Playing: " + data); 7 | // mimics playing music 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/factories/DeviceFactory.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.factories; 2 | 3 | import MusicPlayerApplication.device.IAudioOutputDevice; 4 | import MusicPlayerApplication.device.BluetoothSpeakerAdapter; 5 | import MusicPlayerApplication.device.HeadphonesAdapter; 6 | import MusicPlayerApplication.device.WiredSpeakerAdapter; 7 | import MusicPlayerApplication.external.BluetoothSpeakerAPI; 8 | import MusicPlayerApplication.external.HeadphonesAPI; 9 | import MusicPlayerApplication.external.WiredSpeakerAPI; 10 | import MusicPlayerApplication.enums.DeviceType; 11 | 12 | 13 | public class DeviceFactory { 14 | public static IAudioOutputDevice createDevice(DeviceType deviceType) { 15 | switch (deviceType) { 16 | case BLUETOOTH: 17 | return new BluetoothSpeakerAdapter(new BluetoothSpeakerAPI()); 18 | case WIRED: 19 | return new WiredSpeakerAdapter(new WiredSpeakerAPI()); 20 | case HEADPHONES: 21 | default: 22 | return new HeadphonesAdapter(new HeadphonesAPI()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/folderStructure.txt: -------------------------------------------------------------------------------- 1 | MusicPlayerApplication/ 2 | │ Main.java # Composition root and entry point 3 | │ MusicPlayerApplication.java # High-level application/demo runner 4 | │ MusicPlayerFacade.java # Facade class (orchestrator) 5 | │ 6 | ├── core/ 7 | │ └── AudioEngine.java #Playback engine 8 | │ 9 | ├── strategies/ 10 | │ ├── PlayStrategy.java 11 | │ ├── SequentialPlayStrategy.java 12 | │ ├── RandomPlayStrategy.java 13 | │ └── CustomQueueStrategy.java 14 | │ 15 | ├── enums/ # All shared enum types 16 | │ ├── DeviceType.java # enum class DeviceType { BLUETOOTH, WIRED, HEADPHONES } 17 | │ └── PlayStrategyType.java # enum class PlayStrategyType { SEQUENTIAL, RANDOM, CUSTOM_QUEUE } 18 | │ 19 | ├── models/ 20 | │ ├── Song.java 21 | │ └── Playlist.java 22 | │ 23 | ├── device/ # Audio device interfaces & adapters 24 | │ ├── IAudioOutputDevice.java 25 | │ ├── WiredSpeakerAdapter.java 26 | │ ├── HeadphonesAdapter.java 27 | │ └── BluetoothSpeakerAdapter.java 28 | │ 29 | ├── external/ # External devices 30 | │ ├── WiredSpeakerAPI.java 31 | │ ├── HeadphonesAPI.java 32 | │ └── BluetoothSpeakerAPI.java 33 | │ 34 | ├── factories/ 35 | │ └── DeviceFactory.java # Creates IAudioOutputDevice instances 36 | │ 37 | └── managers/ 38 | ├── PlaylistManager.java 39 | ├── StrategyManager.java 40 | └── DeviceManager.java 41 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/managers/DeviceManager.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.managers; 2 | 3 | import MusicPlayerApplication.device.IAudioOutputDevice; 4 | import MusicPlayerApplication.enums.DeviceType; 5 | import MusicPlayerApplication.factories.DeviceFactory; 6 | 7 | public class DeviceManager { 8 | private static DeviceManager instance = null; 9 | private IAudioOutputDevice currentOutputDevice; 10 | 11 | private DeviceManager() { 12 | currentOutputDevice = null; 13 | } 14 | 15 | public static synchronized DeviceManager getInstance() { 16 | if (instance == null) { 17 | instance = new DeviceManager(); 18 | } 19 | return instance; 20 | } 21 | 22 | public void connect(DeviceType deviceType) { 23 | if (currentOutputDevice != null) { 24 | // In C++: delete currentOutputDevice; 25 | // In Java, garbage collector handles it, so no explicit delete. 26 | } 27 | 28 | currentOutputDevice = DeviceFactory.createDevice(deviceType); 29 | 30 | switch (deviceType) { 31 | case BLUETOOTH: 32 | System.out.println("Bluetooth device connected "); 33 | break; 34 | case WIRED: 35 | System.out.println("Wired device connected "); 36 | break; 37 | case HEADPHONES: 38 | System.out.println("Headphones connected "); 39 | break; 40 | } 41 | } 42 | 43 | public IAudioOutputDevice getOutputDevice() { 44 | if (currentOutputDevice == null) { 45 | throw new RuntimeException("No output device is connected."); 46 | } 47 | return currentOutputDevice; 48 | } 49 | 50 | public boolean hasOutputDevice() { 51 | return currentOutputDevice != null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/managers/PlaylistManager.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.managers; 2 | 3 | import MusicPlayerApplication.models.Playlist; 4 | import MusicPlayerApplication.models.Song; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class PlaylistManager { 10 | private static PlaylistManager instance = null; 11 | private Map playlists; 12 | 13 | private PlaylistManager() { 14 | playlists = new HashMap<>(); 15 | } 16 | 17 | public static synchronized PlaylistManager getInstance() { 18 | if (instance == null) { 19 | instance = new PlaylistManager(); 20 | } 21 | return instance; 22 | } 23 | 24 | public void createPlaylist(String name) { 25 | if (playlists.containsKey(name)) { 26 | throw new RuntimeException("Playlist \"" + name + "\" already exists."); 27 | } 28 | playlists.put(name, new Playlist(name)); 29 | } 30 | 31 | public void addSongToPlaylist(String playlistName, Song song) { 32 | if (!playlists.containsKey(playlistName)) { 33 | throw new RuntimeException("Playlist \"" + playlistName + "\" not found."); 34 | } 35 | playlists.get(playlistName).addSongToPlaylist(song); 36 | } 37 | 38 | public Playlist getPlaylist(String name) { 39 | if (!playlists.containsKey(name)) { 40 | throw new RuntimeException("Playlist \"" + name + "\" not found."); 41 | } 42 | return playlists.get(name); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/managers/StrategyManager.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.managers; 2 | 3 | import MusicPlayerApplication.strategies.SequentialPlayStrategy; 4 | import MusicPlayerApplication.strategies.RandomPlayStrategy; 5 | import MusicPlayerApplication.strategies.CustomQueueStrategy; 6 | import MusicPlayerApplication.strategies.PlayStrategy; 7 | import MusicPlayerApplication.enums.PlayStrategyType; 8 | 9 | public class StrategyManager { 10 | private static StrategyManager instance = null; 11 | private SequentialPlayStrategy sequentialStrategy; 12 | private RandomPlayStrategy randomStrategy; 13 | private CustomQueueStrategy customQueueStrategy; 14 | 15 | private StrategyManager() { 16 | sequentialStrategy = new SequentialPlayStrategy(); 17 | randomStrategy = new RandomPlayStrategy(); 18 | customQueueStrategy = new CustomQueueStrategy(); 19 | } 20 | 21 | public static synchronized StrategyManager getInstance() { 22 | if (instance == null) { 23 | instance = new StrategyManager(); 24 | } 25 | return instance; 26 | } 27 | 28 | public PlayStrategy getStrategy(PlayStrategyType type) { 29 | if (type == PlayStrategyType.SEQUENTIAL) { 30 | return sequentialStrategy; 31 | } else if (type == PlayStrategyType.RANDOM) { 32 | return randomStrategy; 33 | } else { 34 | return customQueueStrategy; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/models/Playlist.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.models; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Playlist { 7 | private String playlistName; 8 | private List songList; 9 | 10 | public Playlist(String name) { 11 | this.playlistName = name; 12 | this.songList = new ArrayList<>(); 13 | } 14 | 15 | public String getPlaylistName() { 16 | return playlistName; 17 | } 18 | 19 | public List getSongs() { 20 | return songList; 21 | } 22 | 23 | public int getSize() { 24 | return songList.size(); 25 | } 26 | 27 | public void addSongToPlaylist(Song song) { 28 | if (song == null) { 29 | throw new RuntimeException("Cannot add null song to playlist."); 30 | } 31 | songList.add(song); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/models/Song.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.models; 2 | 3 | public class Song { 4 | private String title; 5 | private String artist; 6 | private String filePath; 7 | 8 | public Song(String title, String artist, String filePath) { 9 | this.title = title; 10 | this.artist = artist; 11 | this.filePath = filePath; 12 | } 13 | 14 | public String getTitle() { 15 | return title; 16 | } 17 | 18 | public String getArtist() { 19 | return artist; 20 | } 21 | 22 | public String getFilePath() { 23 | return filePath; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/strategies/PlayStrategy.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.strategies; 2 | 3 | import MusicPlayerApplication.models.Playlist; 4 | import MusicPlayerApplication.models.Song; 5 | 6 | public interface PlayStrategy { 7 | void setPlaylist(Playlist playlist); 8 | Song next(); 9 | boolean hasNext(); 10 | Song previous(); 11 | boolean hasPrevious(); 12 | default void addToNext(Song song) {} 13 | } 14 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/strategies/RandomPlayStrategy.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.strategies; 2 | 3 | import MusicPlayerApplication.models.Playlist; 4 | import MusicPlayerApplication.models.Song; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Random; 9 | import java.util.Stack; 10 | 11 | public class RandomPlayStrategy implements PlayStrategy { 12 | private Playlist currentPlaylist; 13 | private List remainingSongs; 14 | private Stack history; 15 | private Random random; 16 | 17 | public RandomPlayStrategy() { 18 | currentPlaylist = null; 19 | random = new Random(); 20 | } 21 | 22 | @Override 23 | public void setPlaylist(Playlist playlist) { 24 | currentPlaylist = playlist; 25 | if (currentPlaylist == null || currentPlaylist.getSize() == 0) return; 26 | 27 | remainingSongs = new ArrayList<>(currentPlaylist.getSongs()); 28 | history = new Stack<>(); 29 | } 30 | 31 | @Override 32 | public boolean hasNext() { 33 | return currentPlaylist != null && !remainingSongs.isEmpty(); 34 | } 35 | 36 | // Next in Loop 37 | @Override 38 | public Song next() { 39 | if (currentPlaylist == null || currentPlaylist.getSize() == 0) { 40 | throw new RuntimeException("No playlist loaded or playlist is empty."); 41 | } 42 | if (remainingSongs.isEmpty()) { 43 | throw new RuntimeException("No songs left to play"); 44 | } 45 | 46 | int idx = random.nextInt(remainingSongs.size()); 47 | Song selectedSong = remainingSongs.get(idx); 48 | 49 | // Remove the selectedSong from the list. (Swap and pop to remove in O(1)) 50 | int lastIndex = remainingSongs.size() - 1; 51 | remainingSongs.set(idx, remainingSongs.get(lastIndex)); 52 | remainingSongs.remove(lastIndex); 53 | 54 | history.push(selectedSong); 55 | return selectedSong; 56 | } 57 | 58 | @Override 59 | public boolean hasPrevious() { 60 | return history.size() > 0; 61 | } 62 | 63 | @Override 64 | public Song previous() { 65 | if (history.isEmpty()) { 66 | throw new RuntimeException("No previous song available."); 67 | } 68 | 69 | Song song = history.pop(); 70 | return song; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Lecture 18/Java Code/MusicPlayerSystem/MusicPlayerApplication/strategies/SequentialPlayStrategy.java: -------------------------------------------------------------------------------- 1 | package MusicPlayerApplication.strategies; 2 | 3 | import MusicPlayerApplication.models.Playlist; 4 | import MusicPlayerApplication.models.Song; 5 | 6 | public class SequentialPlayStrategy implements PlayStrategy { 7 | private Playlist currentPlaylist; 8 | private int currentIndex; 9 | 10 | public SequentialPlayStrategy() { 11 | currentPlaylist = null; 12 | currentIndex = -1; 13 | } 14 | 15 | @Override 16 | public void setPlaylist(Playlist playlist) { 17 | currentPlaylist = playlist; 18 | currentIndex = -1; 19 | } 20 | 21 | @Override 22 | public boolean hasNext() { 23 | return ((currentIndex + 1) < currentPlaylist.getSize()); 24 | } 25 | 26 | // Next in Loop 27 | @Override 28 | public Song next() { 29 | if (currentPlaylist == null || currentPlaylist.getSize() == 0) { 30 | throw new RuntimeException("No playlist loaded or playlist is empty."); 31 | } 32 | currentIndex = currentIndex + 1; 33 | return currentPlaylist.getSongs().get(currentIndex); 34 | } 35 | 36 | @Override 37 | public boolean hasPrevious() { 38 | return (currentIndex - 1 > 0); 39 | } 40 | 41 | // previous in Loop 42 | @Override 43 | public Song previous() { 44 | if (currentPlaylist == null || currentPlaylist.getSize() == 0) { 45 | throw new RuntimeException("No playlist loaded or playlist is empty."); 46 | } 47 | currentIndex = currentIndex - 1; 48 | return currentPlaylist.getSongs().get(currentIndex); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Lecture 18/UML.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 18/UML.pdf -------------------------------------------------------------------------------- /Lecture 19/Example UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 19/Example UML.jpeg -------------------------------------------------------------------------------- /Lecture 19/Standard UML.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/Lecture 19/Standard UML.jpeg -------------------------------------------------------------------------------- /PracticeProblems/PracticeProblem1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/PracticeProblems/PracticeProblem1.pdf -------------------------------------------------------------------------------- /PracticeProblems/PracticeProblem2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adityatandon15/LLD/b0d63e9ca03bbe5e97f120be0830a6dae8e0fbe0/PracticeProblems/PracticeProblem2.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLD 2 | All Codes and Notes for LLD Playlist of Coder Army 3 | --------------------------------------------------------------------------------