diff --git a/Lecture 02/Python Code/Abstraction.py b/Lecture 02/Python Code/Abstraction.py new file mode 100644 index 0000000..e1b76e6 --- /dev/null +++ b/Lecture 02/Python Code/Abstraction.py @@ -0,0 +1,84 @@ +from abc import ABC, abstractmethod + +class Car(ABC): + """ + Car Interface --> Acts as an interface for the outside world to operate the car. + This interface tells 'WHAT' all it can do rather than 'HOW' it does that. + """ + + @abstractmethod + def start_engine(self): + pass + + @abstractmethod + def shift_gear(self, gear): + pass + + @abstractmethod + def accelerate(self): + pass + + @abstractmethod + def brake(self): + pass + + @abstractmethod + def stop_engine(self): + pass + + +class SportsCar(Car): + """ + Concrete class that provides implementation details of the Car interface. + Represents a real-world sports car with all functionalities. + """ + + def __init__(self, brand, model): + self.brand = brand + self.model = model + self.is_engine_on = False + self.current_speed = 0 + self.current_gear = 0 + + def start_engine(self): + self.is_engine_on = True + print(f"{self.brand} {self.model} : Engine starts with a roar!") + + def shift_gear(self, gear): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Engine is off! Cannot shift gear.") + return + self.current_gear = gear + print(f"{self.brand} {self.model} : Shifted to gear {self.current_gear}") + + def accelerate(self): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Engine is off! Cannot accelerate.") + return + self.current_speed += 20 + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h") + + def brake(self): + self.current_speed -= 20 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Braking! Speed is now {self.current_speed} km/h") + + def stop_engine(self): + self.is_engine_on = False + self.current_gear = 0 + self.current_speed = 0 + print(f"{self.brand} {self.model} : Engine turned off.") + + +# Main Method +if __name__ == "__main__": + my_car = SportsCar("Ford", "Mustang") + + my_car.start_engine() + my_car.shift_gear(1) + my_car.accelerate() + my_car.shift_gear(2) + my_car.accelerate() + my_car.brake() + my_car.stop_engine() \ No newline at end of file diff --git a/Lecture 02/Python Code/Encapsulation.py b/Lecture 02/Python Code/Encapsulation.py new file mode 100644 index 0000000..131c769 --- /dev/null +++ b/Lecture 02/Python Code/Encapsulation.py @@ -0,0 +1,74 @@ +class SportsCar: + """ + SportsCar class demonstrates encapsulation by combining characteristics (attributes) + and behaviors (methods) into a single class and restricting access to certain attributes + using private variables and getter/setter methods. + """ + + def __init__(self, brand, model): + self.__brand = brand + self.__model = model + self.__is_engine_on = False + self.__current_speed = 0 + self.__current_gear = 0 + self.__tyre_company = None # Introduced to explain setters + + # Getter for current speed + def get_speed(self): + return self.__current_speed + + # Getter for tyre company + def get_tyre_company(self): + return self.__tyre_company + + # Setter for tyre company + def set_tyre_company(self, tyre_company): + self.__tyre_company = tyre_company + + # Method to start the engine + def start_engine(self): + self.__is_engine_on = True + print(f"{self.__brand} {self.__model} : Engine starts with a roar!") + + # Method to shift gear + def shift_gear(self, gear): + self.__current_gear = gear + print(f"{self.__brand} {self.__model} : Shifted to gear {self.__current_gear}") + + # Method to accelerate + def accelerate(self): + if not self.__is_engine_on: + print(f"{self.__brand} {self.__model} : Engine is off! Cannot accelerate.") + return + self.__current_speed += 20 + print(f"{self.__brand} {self.__model} : Accelerating to {self.__current_speed} km/h") + + # Method to brake + def brake(self): + self.__current_speed -= 20 + if self.__current_speed < 0: + self.__current_speed = 0 + print(f"{self.__brand} {self.__model} : Braking! Speed is now {self.__current_speed} km/h") + + # Method to stop the engine + def stop_engine(self): + self.__is_engine_on = False + self.__current_gear = 0 + self.__current_speed = 0 + print(f"{self.__brand} {self.__model} : Engine turned off.") + + +# Main Method +if __name__ == "__main__": + my_sports_car = SportsCar("Ford", "Mustang") + + my_sports_car.start_engine() + my_sports_car.shift_gear(1) + my_sports_car.accelerate() + my_sports_car.shift_gear(2) + my_sports_car.accelerate() + my_sports_car.brake() + my_sports_car.stop_engine() + + # Accessing speed using getter + print(f"Current Speed of My Sports Car is {my_sports_car.get_speed()}") \ No newline at end of file diff --git a/Lecture 03/Python Code/DynamicPolymorphism.py b/Lecture 03/Python Code/DynamicPolymorphism.py new file mode 100644 index 0000000..e8446a9 --- /dev/null +++ b/Lecture 03/Python Code/DynamicPolymorphism.py @@ -0,0 +1,105 @@ +from abc import ABC, abstractmethod + +class Car(ABC): + """ + Abstract base class representing a generic car. + """ + + def __init__(self, brand, model): + self.brand = brand + self.model = model + self.is_engine_on = False + self.current_speed = 0 + + def start_engine(self): + self.is_engine_on = True + print(f"{self.brand} {self.model} : Engine started.") + + def stop_engine(self): + self.is_engine_on = False + self.current_speed = 0 + print(f"{self.brand} {self.model} : Engine turned off.") + + @abstractmethod + def accelerate(self): + pass + + @abstractmethod + def brake(self): + pass + + +class ManualCar(Car): + """ + Represents a manual car with gear shifting functionality. + """ + + def __init__(self, brand, model): + super().__init__(brand, model) + self.current_gear = 0 + + def shift_gear(self, gear): + self.current_gear = gear + print(f"{self.brand} {self.model} : Shifted to gear {self.current_gear}") + + def accelerate(self): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Cannot accelerate! Engine is off.") + return + self.current_speed += 20 + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h") + + def brake(self): + self.current_speed -= 20 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Braking! Speed is now {self.current_speed} km/h") + + +class ElectricCar(Car): + """ + Represents an electric car with battery functionality. + """ + + def __init__(self, brand, model): + super().__init__(brand, model) + self.battery_level = 100 + + def charge_battery(self): + self.battery_level = 100 + print(f"{self.brand} {self.model} : Battery fully charged!") + + def accelerate(self): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Cannot accelerate! Engine is off.") + return + if self.battery_level <= 0: + print(f"{self.brand} {self.model} : Battery dead! Cannot accelerate.") + return + self.battery_level -= 10 + self.current_speed += 15 + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h. Battery at {self.battery_level}%.") + + def brake(self): + self.current_speed -= 15 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Regenerative braking! Speed is now {self.current_speed} km/h. Battery at {self.battery_level}%.") + + +if __name__ == "__main__": + my_manual_car = ManualCar("Suzuki", "WagonR") + my_manual_car.start_engine() + my_manual_car.accelerate() + my_manual_car.accelerate() + my_manual_car.brake() + my_manual_car.stop_engine() + + print("----------------------") + + my_electric_car = ElectricCar("Tesla", "Model S") + my_electric_car.start_engine() + my_electric_car.accelerate() + my_electric_car.accelerate() + my_electric_car.brake() + my_electric_car.stop_engine() \ No newline at end of file diff --git a/Lecture 03/Python Code/Inheritance.py b/Lecture 03/Python Code/Inheritance.py new file mode 100644 index 0000000..461bcd2 --- /dev/null +++ b/Lecture 03/Python Code/Inheritance.py @@ -0,0 +1,82 @@ +class Car: + """ + Represents a generic car with common attributes and methods. + """ + + def __init__(self, brand, model): + self.brand = brand + self.model = model + self.is_engine_on = False + self.current_speed = 0 + + # Common methods for all cars + def start_engine(self): + self.is_engine_on = True + print(f"{self.brand} {self.model} : Engine started.") + + def stop_engine(self): + self.is_engine_on = False + self.current_speed = 0 + print(f"{self.brand} {self.model} : Engine turned off.") + + def accelerate(self): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Cannot accelerate! Engine is off.") + return + self.current_speed += 20 + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h") + + def brake(self): + self.current_speed -= 20 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Braking! Speed is now {self.current_speed} km/h") + + +class ManualCar(Car): + """ + Represents a manual car with gear-shifting functionality. + """ + + def __init__(self, brand, model): + super().__init__(brand, model) + self.current_gear = 0 + + # Specialized method for Manual Car + def shift_gear(self, gear): + self.current_gear = gear + print(f"{self.brand} {self.model} : Shifted to gear {self.current_gear}") + + +class ElectricCar(Car): + """ + Represents an electric car with battery functionality. + """ + + def __init__(self, brand, model): + super().__init__(brand, model) + self.battery_level = 100 + + # Specialized method for Electric Car + def charge_battery(self): + self.battery_level = 100 + print(f"{self.brand} {self.model} : Battery fully charged!") + + +# Main Method +if __name__ == "__main__": + my_manual_car = ManualCar("Suzuki", "WagonR") + my_manual_car.start_engine() + my_manual_car.shift_gear(1) # Specific to Manual Car + my_manual_car.accelerate() + my_manual_car.brake() + my_manual_car.stop_engine() + + print("----------------------") + + my_electric_car = ElectricCar("Tesla", "Model S") + my_electric_car.charge_battery() # Specific to Electric Car + my_electric_car.start_engine() + my_electric_car.accelerate() + my_electric_car.brake() + my_electric_car.stop_engine() \ No newline at end of file diff --git a/Lecture 03/Python Code/StaticAndDynamicPolymorphism.py b/Lecture 03/Python Code/StaticAndDynamicPolymorphism.py new file mode 100644 index 0000000..6539d25 --- /dev/null +++ b/Lecture 03/Python Code/StaticAndDynamicPolymorphism.py @@ -0,0 +1,112 @@ +from abc import ABC, abstractmethod + +class Car(ABC): + """ + Abstract base class representing a generic car. + """ + + def __init__(self, brand, model): + self.brand = brand + self.model = model + self.is_engine_on = False + self.current_speed = 0 + + def start_engine(self): + self.is_engine_on = True + print(f"{self.brand} {self.model} : Engine started.") + + def stop_engine(self): + self.is_engine_on = False + self.current_speed = 0 + print(f"{self.brand} {self.model} : Engine turned off.") + + @abstractmethod + def accelerate(self, speed=None): + pass + + @abstractmethod + def brake(self): + pass + + +class ManualCar(Car): + """ + Represents a manual car with gear shifting functionality. + """ + + def __init__(self, brand, model): + super().__init__(brand, model) + self.current_gear = 0 + + def shift_gear(self, gear): + self.current_gear = gear + print(f"{self.brand} {self.model} : Shifted to gear {self.current_gear}") + + def accelerate(self, speed=None): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Cannot accelerate! Engine is off.") + return + if speed is None: + self.current_speed += 20 + else: + self.current_speed += speed + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h") + + def brake(self): + self.current_speed -= 20 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Braking! Speed is now {self.current_speed} km/h") + + +class ElectricCar(Car): + """ + Represents an electric car with battery functionality. + """ + + def __init__(self, brand, model): + super().__init__(brand, model) + self.battery_level = 100 + + def charge_battery(self): + self.battery_level = 100 + print(f"{self.brand} {self.model} : Battery fully charged!") + + def accelerate(self, speed=None): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Cannot accelerate! Engine is off.") + return + if self.battery_level <= 0: + print(f"{self.brand} {self.model} : Battery dead! Cannot accelerate.") + return + if speed is None: + self.battery_level -= 10 + self.current_speed += 15 + else: + self.battery_level -= 10 + speed + self.current_speed += speed + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h. Battery at {self.battery_level}%.") + + def brake(self): + self.current_speed -= 15 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Regenerative braking! Speed is now {self.current_speed} km/h. Battery at {self.battery_level}%.") + + +if __name__ == "__main__": + my_manual_car = ManualCar("Ford", "Mustang") + my_manual_car.start_engine() + my_manual_car.accelerate() + my_manual_car.accelerate(30) + my_manual_car.brake() + my_manual_car.stop_engine() + + print("----------------------") + + my_electric_car = ElectricCar("Tesla", "Model S") + my_electric_car.start_engine() + my_electric_car.accelerate() + my_electric_car.accelerate(25) + my_electric_car.brake() + my_electric_car.stop_engine() \ No newline at end of file diff --git a/Lecture 03/Python Code/StaticPolymorphism.py b/Lecture 03/Python Code/StaticPolymorphism.py new file mode 100644 index 0000000..eb7efac --- /dev/null +++ b/Lecture 03/Python Code/StaticPolymorphism.py @@ -0,0 +1,49 @@ +class ManualCar: + """ + Represents a manual car demonstrating static polymorphism (method overloading). + """ + + def __init__(self, brand, model): + self.brand = brand + self.model = model + self.is_engine_on = False + self.current_speed = 0 + self.current_gear = 0 + + def start_engine(self): + self.is_engine_on = True + print(f"{self.brand} {self.model} : Engine started.") + + def stop_engine(self): + self.is_engine_on = False + self.current_speed = 0 + print(f"{self.brand} {self.model} : Engine turned off.") + + def accelerate(self, speed=None): + if not self.is_engine_on: + print(f"{self.brand} {self.model} : Cannot accelerate! Engine is off.") + return + if speed is None: + self.current_speed += 20 + else: + self.current_speed += speed + print(f"{self.brand} {self.model} : Accelerating to {self.current_speed} km/h") + + def brake(self): + self.current_speed -= 20 + if self.current_speed < 0: + self.current_speed = 0 + print(f"{self.brand} {self.model} : Braking! Speed is now {self.current_speed} km/h") + + def shift_gear(self, gear): + self.current_gear = gear + print(f"{self.brand} {self.model} : Shifted to gear {self.current_gear}") + + +if __name__ == "__main__": + my_manual_car = ManualCar("Suzuki", "WagonR") + my_manual_car.start_engine() + my_manual_car.accelerate() + my_manual_car.accelerate(40) + my_manual_car.brake() + my_manual_car.stop_engine() \ No newline at end of file diff --git a/Lecture 05/Python Code/LSP/LSPFollowed.py b/Lecture 05/Python Code/LSP/LSPFollowed.py new file mode 100644 index 0000000..931f77c --- /dev/null +++ b/Lecture 05/Python Code/LSP/LSPFollowed.py @@ -0,0 +1,70 @@ +from abc import ABC, abstractmethod + +# DepositOnlyAccount interface +class DepositOnlyAccount(ABC): + @abstractmethod + def deposit(self, amount): + pass + +# WithdrawableAccount interface +class WithdrawableAccount(DepositOnlyAccount): + @abstractmethod + def withdraw(self, amount): + pass + +class SavingAccount(WithdrawableAccount): + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount} in Savings Account. New Balance: {self.balance}") + + def withdraw(self, amount): + if self.balance >= amount: + self.balance -= amount + print(f"Withdrawn: {amount} from Savings Account. New Balance: {self.balance}") + else: + print("Insufficient funds in Savings Account!") + +class CurrentAccount(WithdrawableAccount): + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount} in Current Account. New Balance: {self.balance}") + + def withdraw(self, amount): + if self.balance >= amount: + self.balance -= amount + print(f"Withdrawn: {amount} from Current Account. New Balance: {self.balance}") + else: + print("Insufficient funds in Current Account!") + +class FixedTermAccount(DepositOnlyAccount): + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount} in Fixed Term Account. New Balance: {self.balance}") + +class BankClient: + def __init__(self, withdrawable_accounts, deposit_only_accounts): + self.withdrawable_accounts = withdrawable_accounts + self.deposit_only_accounts = deposit_only_accounts + + def process_transactions(self): + for acc in self.withdrawable_accounts: + acc.deposit(1000) + acc.withdraw(500) + for acc in self.deposit_only_accounts: + acc.deposit(5000) + +if __name__ == "__main__": + withdrawable_accounts = [SavingAccount(), CurrentAccount()] + deposit_only_accounts = [FixedTermAccount()] + + client = BankClient(withdrawable_accounts, deposit_only_accounts) + client.process_transactions() \ No newline at end of file diff --git a/Lecture 05/Python Code/LSP/LSPFollowedWrongly.py b/Lecture 05/Python Code/LSP/LSPFollowedWrongly.py new file mode 100644 index 0000000..6544b38 --- /dev/null +++ b/Lecture 05/Python Code/LSP/LSPFollowedWrongly.py @@ -0,0 +1,35 @@ +class Account: + """ + Represents a generic account with deposit and withdraw functionality. + """ + + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount}. New Balance: {self.balance}") + + def withdraw(self, amount): + if self.balance >= amount: + self.balance -= amount + print(f"Withdrawn: {amount}. New Balance: {self.balance}") + else: + print("Insufficient funds!") + +class FixedTermAccount(Account): + """ + Represents a fixed-term account where withdrawal is not allowed. + """ + + def withdraw(self, amount): + print("Withdrawal not allowed in Fixed Term Account!") + +if __name__ == "__main__": + saving_account = Account() + saving_account.deposit(1000) + saving_account.withdraw(500) + + fixed_term_account = FixedTermAccount() + fixed_term_account.deposit(2000) + fixed_term_account.withdraw(500) # Violates LSP as withdrawal is not allowed \ No newline at end of file diff --git a/Lecture 05/Python Code/LSP/LSPViolated.py b/Lecture 05/Python Code/LSP/LSPViolated.py new file mode 100644 index 0000000..c284055 --- /dev/null +++ b/Lecture 05/Python Code/LSP/LSPViolated.py @@ -0,0 +1,68 @@ +from abc import ABC, abstractmethod + +class Account(ABC): + @abstractmethod + def deposit(self, amount): + pass + + @abstractmethod + def withdraw(self, amount): + pass + +class SavingAccount(Account): + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount} in Savings Account. New Balance: {self.balance}") + + def withdraw(self, amount): + if self.balance >= amount: + self.balance -= amount + print(f"Withdrawn: {amount} from Savings Account. New Balance: {self.balance}") + else: + print("Insufficient funds in Savings Account!") + +class CurrentAccount(Account): + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount} in Current Account. New Balance: {self.balance}") + + def withdraw(self, amount): + if self.balance >= amount: + self.balance -= amount + print(f"Withdrawn: {amount} from Current Account. New Balance: {self.balance}") + else: + print("Insufficient funds in Current Account!") + +class FixedTermAccount(Account): + def __init__(self): + self.balance = 0 + + def deposit(self, amount): + self.balance += amount + print(f"Deposited: {amount} in Fixed Term Account. New Balance: {self.balance}") + + def withdraw(self, amount): + raise NotImplementedError("Withdrawal not allowed in Fixed Term Account!") + +class BankClient: + def __init__(self, accounts): + self.accounts = accounts + + def process_transactions(self): + for acc in self.accounts: + acc.deposit(1000) + try: + acc.withdraw(500) + except NotImplementedError as e: + print(f"Exception: {e}") + +if __name__ == "__main__": + accounts = [SavingAccount(), CurrentAccount(), FixedTermAccount()] + client = BankClient(accounts) + client.process_transactions() \ No newline at end of file diff --git a/Lecture 05/Python Code/OCP/OCPFollowed.py b/Lecture 05/Python Code/OCP/OCPFollowed.py new file mode 100644 index 0000000..623ed4d --- /dev/null +++ b/Lecture 05/Python Code/OCP/OCPFollowed.py @@ -0,0 +1,58 @@ +from abc import ABC, abstractmethod +class Product: + def __init__(self, name, price): + self.name = name + self.price = price + +class ShoppingCart: + def __init__(self): + self.products = [] + + def add_product(self, product): + self.products.append(product) + + def calculate_total(self): + return sum(p.price for p in self.products) + +class ShoppingCartPrinter: + def __init__(self, cart): + self.cart = cart + + def print_invoice(self): + print("Shopping Cart Invoice:") + for product in self.cart.products: + print(f"{product.name} - Rs {product.price}") + print(f"Total: Rs {self.cart.calculate_total()}") + +class Persistence(ABC): + @abstractmethod + def save(self, cart): + pass + +class SQLPersistence(Persistence): + def save(self, cart): + print("Saving shopping cart to SQL DB...") + +class MongoPersistence(Persistence): + def save(self, cart): + print("Saving shopping cart to MongoDB...") + +class FilePersistence(Persistence): + def save(self, cart): + print("Saving shopping cart to a file...") + +if __name__ == "__main__": + cart = ShoppingCart() + cart.add_product(Product("Laptop", 50000)) + cart.add_product(Product("Mouse", 2000)) + + printer = ShoppingCartPrinter(cart) + printer.print_invoice() + + sql_persistence = SQLPersistence() + mongo_persistence = MongoPersistence() + file_persistence = FilePersistence() + + sql_persistence.save(cart) + mongo_persistence.save(cart) + file_persistence.save(cart) \ No newline at end of file diff --git a/Lecture 05/Python Code/OCP/OCPFollowedWrongly.py b/Lecture 05/Python Code/OCP/OCPFollowedWrongly.py new file mode 100644 index 0000000..53abd74 --- /dev/null +++ b/Lecture 05/Python Code/OCP/OCPFollowedWrongly.py @@ -0,0 +1,36 @@ +class Product: + def __init__(self, name, price): + self.name = name + self.price = price + +class ShoppingCart: + """ + Represents a shopping cart that violates OCP by hardcoding persistence methods. + """ + + def __init__(self): + self.products = [] + + def add_product(self, product): + self.products.append(product) + + def calculate_total(self): + return sum(p.price for p in self.products) + + def save_to_sql_database(self): + print("Saving shopping cart to SQL DB...") + + def save_to_mongo_database(self): + print("Saving shopping cart to MongoDB...") + + def save_to_file(self): + print("Saving shopping cart to a file...") + +if __name__ == "__main__": + cart = ShoppingCart() + cart.add_product(Product("Laptop", 50000)) + cart.add_product(Product("Mouse", 2000)) + + cart.save_to_sql_database() + cart.save_to_mongo_database() + cart.save_to_file() \ No newline at end of file diff --git a/Lecture 05/Python Code/OCP/OCPViolated.py b/Lecture 05/Python Code/OCP/OCPViolated.py new file mode 100644 index 0000000..8b9c71d --- /dev/null +++ b/Lecture 05/Python Code/OCP/OCPViolated.py @@ -0,0 +1,32 @@ +class Product: + def __init__(self, name, price): + self.name = name + self.price = price + +class ShoppingCart: + def __init__(self): + self.products = [] + + def add_product(self, product): + self.products.append(product) + + def calculate_total(self): + return sum(p.price for p in self.products) + + def save_to_sql_database(self): + print("Saving shopping cart to SQL DB...") + + def save_to_mongo_database(self): + print("Saving shopping cart to MongoDB...") + + def save_to_file(self): + print("Saving shopping cart to a file...") + +if __name__ == "__main__": + cart = ShoppingCart() + cart.add_product(Product("Laptop", 50000)) + cart.add_product(Product("Mouse", 2000)) + + cart.save_to_sql_database() + cart.save_to_mongo_database() + cart.save_to_file() \ No newline at end of file diff --git a/Lecture 05/Python Code/SRP/SRPFollowed.py b/Lecture 05/Python Code/SRP/SRPFollowed.py new file mode 100644 index 0000000..6c8290c --- /dev/null +++ b/Lecture 05/Python Code/SRP/SRPFollowed.py @@ -0,0 +1,42 @@ +class Product: + def __init__(self, name, price): + self.name = name + self.price = price + +class ShoppingCart: + def __init__(self): + self.products = [] + + def add_product(self, product): + self.products.append(product) + + def calculate_total(self): + return sum(p.price for p in self.products) + +class ShoppingCartPrinter: + def __init__(self, cart): + self.cart = cart + + def print_invoice(self): + print("Shopping Cart Invoice:") + for product in self.cart.products: + print(f"{product.name} - Rs {product.price}") + print(f"Total: Rs {self.cart.calculate_total()}") + +class ShoppingCartStorage: + def __init__(self, cart): + self.cart = cart + + def save_to_database(self): + print("Saving shopping cart to database...") + +if __name__ == "__main__": + cart = ShoppingCart() + cart.add_product(Product("Laptop", 50000)) + cart.add_product(Product("Mouse", 2000)) + + printer = ShoppingCartPrinter(cart) + printer.print_invoice() + + storage = ShoppingCartStorage(cart) + storage.save_to_database() \ No newline at end of file diff --git a/Lecture 05/Python Code/SRP/SRPFollowedWrongly.py b/Lecture 05/Python Code/SRP/SRPFollowedWrongly.py new file mode 100644 index 0000000..9a9c6de --- /dev/null +++ b/Lecture 05/Python Code/SRP/SRPFollowedWrongly.py @@ -0,0 +1,35 @@ +class Product: + def __init__(self, name, price): + self.name = name + self.price = price + +class ShoppingCart: + """ + Represents a shopping cart that violates SRP by handling multiple responsibilities. + """ + + def __init__(self): + self.products = [] + + def add_product(self, product): + self.products.append(product) + + def calculate_total(self): + return sum(p.price for p in self.products) + + def print_invoice(self): + print("Shopping Cart Invoice:") + for product in self.products: + print(f"{product.name} - Rs {product.price}") + print(f"Total: Rs {self.calculate_total()}") + + def save_to_database(self): + print("Saving shopping cart to database...") + +if __name__ == "__main__": + cart = ShoppingCart() + cart.add_product(Product("Laptop", 50000)) + cart.add_product(Product("Mouse", 2000)) + + cart.print_invoice() + cart.save_to_database() \ No newline at end of file diff --git a/Lecture 05/Python Code/SRP/SRPViolated.py b/Lecture 05/Python Code/SRP/SRPViolated.py new file mode 100644 index 0000000..0d7c232 --- /dev/null +++ b/Lecture 05/Python Code/SRP/SRPViolated.py @@ -0,0 +1,31 @@ +class Product: + def __init__(self, name, price): + self.name = name + self.price = price + +class ShoppingCart: + def __init__(self): + self.products = [] + + def add_product(self, product): + self.products.append(product) + + def calculate_total(self): + return sum(p.price for p in self.products) + + def print_invoice(self): + print("Shopping Cart Invoice:") + for product in self.products: + print(f"{product.name} - Rs {product.price}") + print(f"Total: Rs {self.calculate_total()}") + + def save_to_database(self): + print("Saving shopping cart to database...") + +if __name__ == "__main__": + cart = ShoppingCart() + cart.add_product(Product("Laptop", 50000)) + cart.add_product(Product("Mouse", 2000)) + + cart.print_invoice() + cart.save_to_database() \ No newline at end of file diff --git a/Lecture 06/Python Code/DIP/DIPFollowed.py b/Lecture 06/Python Code/DIP/DIPFollowed.py new file mode 100644 index 0000000..bfae589 --- /dev/null +++ b/Lecture 06/Python Code/DIP/DIPFollowed.py @@ -0,0 +1,32 @@ +# Abstraction (Interface) +class Database: + def save(self, data): + pass + +# MySQL implementation (Low-level module) +class MySQLDatabase(Database): + def save(self, data): + print(f"Executing SQL Query: INSERT INTO users VALUES('{data}');") + +# MongoDB implementation (Low-level module) +class MongoDBDatabase(Database): + def save(self, data): + print(f"Executing MongoDB Function: db.users.insert({{name: '{data}'}})") + +# High-level module (Now loosely coupled via Dependency Injection) +class UserService: + def __init__(self, database: Database): + self.db = database + + def store_user(self, user): + self.db.save(user) + +if __name__ == "__main__": + mysql = MySQLDatabase() + mongodb = MongoDBDatabase() + + service1 = UserService(mysql) + service1.store_user("Aditya") + + service2 = UserService(mongodb) + service2.store_user("Rohit") \ No newline at end of file diff --git a/Lecture 06/Python Code/DIP/DIPViolated.py b/Lecture 06/Python Code/DIP/DIPViolated.py new file mode 100644 index 0000000..61849d0 --- /dev/null +++ b/Lecture 06/Python Code/DIP/DIPViolated.py @@ -0,0 +1,23 @@ +class MySQLDatabase: # Low-level module + def save_to_sql(self, data): + print(f"Executing SQL Query: INSERT INTO users VALUES('{data}');") + +class MongoDBDatabase: # Low-level module + def save_to_mongo(self, data): + print(f"Executing MongoDB Function: db.users.insert({{name: '{data}'}})") + +class UserService: # High-level module (Tightly coupled) + def __init__(self): + self.sql_db = MySQLDatabase() + self.mongo_db = MongoDBDatabase() + + def store_user_to_sql(self, user): + self.sql_db.save_to_sql(user) + + def store_user_to_mongo(self, user): + self.mongo_db.save_to_mongo(user) + +if __name__ == "__main__": + service = UserService() + service.store_user_to_sql("Aditya") + service.store_user_to_mongo("Rohit") \ No newline at end of file diff --git a/Lecture 06/Python Code/ISP/ISPFollowed.py b/Lecture 06/Python Code/ISP/ISPFollowed.py new file mode 100644 index 0000000..aae6e89 --- /dev/null +++ b/Lecture 06/Python Code/ISP/ISPFollowed.py @@ -0,0 +1,50 @@ +# Separate interface for 2D shapes +class TwoDimensionalShape: + def area(self): + pass + +# Separate interface for 3D shapes +class ThreeDimensionalShape: + def area(self): + pass + + def volume(self): + pass + +# Square implements only the 2D interface +class Square(TwoDimensionalShape): + def __init__(self, side): + self.side = side + + def area(self): + return self.side * self.side + +# Rectangle implements only the 2D interface +class Rectangle(TwoDimensionalShape): + def __init__(self, length, width): + self.length = length + self.width = width + + def area(self): + return self.length * self.width + +# Cube implements the 3D interface +class Cube(ThreeDimensionalShape): + def __init__(self, side): + self.side = side + + def area(self): + return 6 * self.side * self.side + + def volume(self): + return self.side * self.side * self.side + +if __name__ == "__main__": + square = Square(5) + rectangle = Rectangle(4, 6) + cube = Cube(3) + + print("Square Area:", square.area()) + print("Rectangle Area:", rectangle.area()) + print("Cube Area:", cube.area()) + print("Cube Volume:", cube.volume()) \ No newline at end of file diff --git a/Lecture 06/Python Code/ISP/ISPViolated.py b/Lecture 06/Python Code/ISP/ISPViolated.py new file mode 100644 index 0000000..798e7ec --- /dev/null +++ b/Lecture 06/Python Code/ISP/ISPViolated.py @@ -0,0 +1,56 @@ +# Single interface for all shapes (Violates ISP) +class Shape: + def area(self): + pass + + def volume(self): + pass # 2D shapes don't have volume! + +# Square is a 2D shape but is forced to implement volume() +class Square(Shape): + def __init__(self, side): + self.side = side + + def area(self): + return self.side * self.side + + def volume(self): + raise NotImplementedError("Volume not applicable for Square") + +# Rectangle is also a 2D shape but is forced to implement volume() +class Rectangle(Shape): + def __init__(self, length, width): + self.length = length + self.width = width + + def area(self): + return self.length * self.width + + def volume(self): + raise NotImplementedError("Volume not applicable for Rectangle") + +# Cube is a 3D shape, so it actually has a volume +class Cube(Shape): + def __init__(self, side): + self.side = side + + def area(self): + return 6 * self.side * self.side + + def volume(self): + return self.side * self.side * self.side + +if __name__ == "__main__": + square = Square(5) + rectangle = Rectangle(4, 6) + cube = Cube(3) + + print("Square Area:", square.area()) + print("Rectangle Area:", rectangle.area()) + print("Cube Area:", cube.area()) + print("Cube Volume:", cube.volume()) + + try: + print("Square Volume:", square.volume()) # Will raise an exception + except NotImplementedError as e: + print("Exception:", e) \ No newline at end of file diff --git a/Lecture 06/Python Code/LSP-Rules/MethodRules/PostConditions.py b/Lecture 06/Python Code/LSP-Rules/MethodRules/PostConditions.py new file mode 100644 index 0000000..9ce8e09 --- /dev/null +++ b/Lecture 06/Python Code/LSP-Rules/MethodRules/PostConditions.py @@ -0,0 +1,32 @@ +# A Postcondition must be satisfied after a method is executed. +# Subclasses can strengthen the Postcondition but cannot weaken it. + +class Car: + def __init__(self): + self.speed = 0 + + def accelerate(self): + print("Accelerating") + self.speed += 20 + + # PostCondition: Speed must reduce after brake + def brake(self): + print("Applying brakes") + self.speed -= 20 + +# Subclass can strengthen postcondition - Does not violate LSP +class HybridCar(Car): + def __init__(self): + super().__init__() + self.charge = 0 + + # PostCondition: Speed must reduce after brake + # PostCondition: Charge must increase. + def brake(self): + print("Applying brakes") + self.speed -= 20 + self.charge += 10 + +if __name__ == "__main__": + hybrid_car = HybridCar() + hybrid_car.brake() # Works fine: HybridCar reduces speed and also increases charge \ No newline at end of file diff --git a/Lecture 06/Python Code/LSP-Rules/MethodRules/PreConditions.py b/Lecture 06/Python Code/LSP-Rules/MethodRules/PreConditions.py new file mode 100644 index 0000000..010d07c --- /dev/null +++ b/Lecture 06/Python Code/LSP-Rules/MethodRules/PreConditions.py @@ -0,0 +1,20 @@ +# A Precondition must be satisfied before a method can be executed. +# Subclasses can weaken the precondition but cannot strengthen it. + +class User: + # Precondition: Password must be at least 8 characters long + def set_password(self, password): + if len(password) < 8: + raise ValueError("Password must be at least 8 characters long!") + print("Password set successfully") + +class AdminUser(User): + # Precondition: Password must be at least 6 characters + def set_password(self, password): + if len(password) < 6: + raise ValueError("Password must be at least 6 characters long!") + print("Password set successfully") + +if __name__ == "__main__": + user = AdminUser() + user.set_password("Admin1") # Works fine: AdminUser allows shorter passwords \ No newline at end of file diff --git a/Lecture 06/Python Code/LSP-Rules/PropertiesRules/ClassInvariants.py b/Lecture 06/Python Code/LSP-Rules/PropertiesRules/ClassInvariants.py new file mode 100644 index 0000000..cd4019f --- /dev/null +++ b/Lecture 06/Python Code/LSP-Rules/PropertiesRules/ClassInvariants.py @@ -0,0 +1,25 @@ +# Invariant: Balance cannot be negative +class BankAccount: + def __init__(self, balance): + if balance < 0: + raise ValueError("Balance can't be negative") + self.balance = balance + + def withdraw(self, amount): + if self.balance - amount < 0: + raise RuntimeError("Insufficient funds") + self.balance -= amount + print(f"Amount withdrawn. Remaining balance is {self.balance}") + +# Breaks invariant: Should not be allowed. +class CheatAccount(BankAccount): + def withdraw(self, amount): + self.balance -= amount # LSP break! Negative balance allowed + print(f"Amount withdrawn. Remaining balance is {self.balance}") + +if __name__ == "__main__": + account = BankAccount(100) + account.withdraw(50) + + cheat_account = CheatAccount(100) + cheat_account.withdraw(150) # Violates invariant \ No newline at end of file diff --git a/Lecture 06/Python Code/LSP-Rules/PropertiesRules/HistoryConstraint.py b/Lecture 06/Python Code/LSP-Rules/PropertiesRules/HistoryConstraint.py new file mode 100644 index 0000000..198227a --- /dev/null +++ b/Lecture 06/Python Code/LSP-Rules/PropertiesRules/HistoryConstraint.py @@ -0,0 +1,27 @@ +# Subclass methods should not allow state changes that the base class never allowed. + +class BankAccount: + def __init__(self, balance): + if balance < 0: + raise ValueError("Balance can't be negative") + self.balance = balance + + def withdraw(self, amount): + if self.balance - amount < 0: + raise RuntimeError("Insufficient funds") + self.balance -= amount + print(f"Amount withdrawn. Remaining balance is {self.balance}") + +class FixedDepositAccount(BankAccount): + def withdraw(self, amount): + raise RuntimeError("Withdraw not allowed in Fixed Deposit") # LSP break! + +if __name__ == "__main__": + account = BankAccount(100) + account.withdraw(50) + + fixed_account = FixedDepositAccount(100) + try: + fixed_account.withdraw(50) # Violates history constraint + except RuntimeError as e: + print(f"Exception: {e}") \ No newline at end of file diff --git a/Lecture 06/Python Code/LSP-Rules/SignatureRules/MethodArgumentRule.py b/Lecture 06/Python Code/LSP-Rules/SignatureRules/MethodArgumentRule.py new file mode 100644 index 0000000..38d01f3 --- /dev/null +++ b/Lecture 06/Python Code/LSP-Rules/SignatureRules/MethodArgumentRule.py @@ -0,0 +1,24 @@ +# Method Argument Rule: +# Subtype method arguments can be identical or wider than the supertype. + +class Parent: + def print(self, msg): + print(f"Parent: {msg}") + +class Child(Parent): + def print(self, msg): + print(f"Child: {msg}") + +class Client: + def __init__(self, parent): + self.parent = parent + + def print_msg(self): + self.parent.print("Hello") + +if __name__ == "__main__": + parent = Parent() + child = Child() + + client = Client(child) + client.print_msg() \ No newline at end of file diff --git a/Lecture 07/Python Code/Bad Design/DocumentEditorClient.py b/Lecture 07/Python Code/Bad Design/DocumentEditorClient.py new file mode 100644 index 0000000..b3fbd01 --- /dev/null +++ b/Lecture 07/Python Code/Bad Design/DocumentEditorClient.py @@ -0,0 +1,50 @@ +import os + +class DocumentEditor: + def __init__(self): + self.document_elements = [] + self.rendered_document = "" + + # Adds text as a plain string + def add_text(self, text): + self.document_elements.append(text) + + # Adds an image represented by its file path + def add_image(self, image_path): + self.document_elements.append(image_path) + + # Renders the document by checking the type of each element at runtime + def render_document(self): + if not self.rendered_document: + result = [] + for element in self.document_elements: + if len(element) > 4 and (element.endswith(".jpg") or element.endswith(".png")): + result.append(f"[Image: {element}]") + else: + result.append(element) + self.rendered_document = "\n".join(result) + return self.rendered_document + + # Saves the rendered document to a file + def save_to_file(self): + try: + with open("document.txt", "w") as writer: + writer.write(self.render_document()) + print("Document saved to document.txt") + except IOError: + print("Error: Unable to open file for writing.") + +class DocumentEditorClient: + @staticmethod + def main(): + editor = DocumentEditor() + editor.add_text("Hello, world!") + editor.add_image("picture.jpg") + editor.add_text("This is a document editor.") + + print(editor.render_document()) + + editor.save_to_file() + +if __name__ == "__main__": + DocumentEditorClient.main() \ No newline at end of file diff --git a/Lecture 07/Python Code/Good Design/DocumentEditorClient.py b/Lecture 07/Python Code/Good Design/DocumentEditorClient.py new file mode 100644 index 0000000..a86335b --- /dev/null +++ b/Lecture 07/Python Code/Good Design/DocumentEditorClient.py @@ -0,0 +1,122 @@ +from abc import ABC, abstractmethod + +# Interface for document elements +class DocumentElement(ABC): + @abstractmethod + def render(self): + pass + +# Concrete implementation for text elements +class TextElement(DocumentElement): + def __init__(self, text): + self.text = text + + def render(self): + return self.text + +# Concrete implementation for image elements +class ImageElement(DocumentElement): + def __init__(self, image_path): + self.image_path = image_path + + def render(self): + return f"[Image: {self.image_path}]" + +# NewLineElement represents a line break in the document. +class NewLineElement(DocumentElement): + def render(self): + return "\n" + +# TabSpaceElement represents a tab space in the document. +class TabSpaceElement(DocumentElement): + def render(self): + return "\t" + +# Document class responsible for holding a collection of elements +class Document: + def __init__(self): + self.document_elements = [] + + def add_element(self, element): + self.document_elements.append(element) + + # Renders the document by concatenating the render output of all elements. + def render(self): + return "".join([element.render() for element in self.document_elements]) + +# Persistence Interface +class Persistence(ABC): + @abstractmethod + def save(self, data): + pass + +# FileStorage implementation of Persistence +class FileStorage(Persistence): + def save(self, data): + try: + with open("document.txt", "w") as out_file: + out_file.write(data) + print("Document saved to document.txt") + except IOError: + print("Error: Unable to open file for writing.") + +# Placeholder DBStorage implementation +class DBStorage(Persistence): + def save(self, data): + # Save to DB (placeholder) + print("Data saved to database (placeholder).") + +# DocumentEditor class managing client interactions +class DocumentEditor: + def __init__(self, document, storage): + self.document = document + self.storage = storage + self.rendered_document = "" + + def add_text(self, text): + self.document.add_element(TextElement(text)) + + def add_image(self, image_path): + self.document.add_element(ImageElement(image_path)) + + # Adds a new line to the document. + def add_new_line(self): + self.document.add_element(NewLineElement()) + + # Adds a tab space to the document. + def add_tab_space(self): + self.document.add_element(TabSpaceElement()) + + def render_document(self): + if not self.rendered_document: + self.rendered_document = self.document.render() + return self.rendered_document + + def save_document(self): + self.storage.save(self.render_document()) + +class DocumentEditorClient: + @staticmethod + def main(): + document = Document() + persistence = FileStorage() + + editor = DocumentEditor(document, persistence) + + # Simulate a client using the editor with common text formatting features. + editor.add_text("Hello, world!") + editor.add_new_line() + editor.add_text("This is a real-world document editor example.") + editor.add_new_line() + editor.add_tab_space() + editor.add_text("Indented text after a tab space.") + editor.add_new_line() + editor.add_image("picture.jpg") + + # Render and display the final document. + print(editor.render_document()) + + editor.save_document() + +if __name__ == "__main__": + DocumentEditorClient.main() \ No newline at end of file diff --git a/Lecture 08/Python Code/StrategyDesignPattern.py b/Lecture 08/Python Code/StrategyDesignPattern.py new file mode 100644 index 0000000..b1bc83b --- /dev/null +++ b/Lecture 08/Python Code/StrategyDesignPattern.py @@ -0,0 +1,91 @@ +from abc import ABC, abstractmethod + +# --- Strategy Interface for Walk --- +class WalkableRobot(ABC): + @abstractmethod + def walk(self): + pass + +# --- Concrete Strategies for Walk --- +class NormalWalk(WalkableRobot): + def walk(self): + print("Walking normally...") + +class NoWalk(WalkableRobot): + def walk(self): + print("Cannot walk.") + +# --- Strategy Interface for Talk --- +class TalkableRobot(ABC): + @abstractmethod + def talk(self): + pass + +# --- Concrete Strategies for Talk --- +class NormalTalk(TalkableRobot): + def talk(self): + print("Talking normally...") + +class NoTalk(TalkableRobot): + def talk(self): + print("Cannot talk.") + +# --- Strategy Interface for Fly --- +class FlyableRobot(ABC): + @abstractmethod + def fly(self): + pass + +# --- Concrete Strategies for Fly --- +class NormalFly(FlyableRobot): + def fly(self): + print("Flying normally...") + +class NoFly(FlyableRobot): + def fly(self): + print("Cannot fly.") + +# --- Robot Base Class --- +class Robot(ABC): + def __init__(self, walk_behavior: WalkableRobot, talk_behavior: TalkableRobot, fly_behavior: FlyableRobot): + self.walk_behavior = walk_behavior + self.talk_behavior = talk_behavior + self.fly_behavior = fly_behavior + + def walk(self): + self.walk_behavior.walk() + + def talk(self): + self.talk_behavior.talk() + + def fly(self): + self.fly_behavior.fly() + + @abstractmethod + def projection(self): + pass + +# --- Concrete Robot Types --- +class CompanionRobot(Robot): + def projection(self): + print("Displaying friendly companion features...") + +class WorkerRobot(Robot): + def projection(self): + print("Displaying worker efficiency stats...") + +# --- Main Function --- +if __name__ == "__main__": + robot1 = CompanionRobot(NormalWalk(), NormalTalk(), NoFly()) + robot1.walk() + robot1.talk() + robot1.fly() + robot1.projection() + + print("--------------------") + + robot2 = WorkerRobot(NoWalk(), NoTalk(), NormalFly()) + robot2.walk() + robot2.talk() + robot2.fly() + robot2.projection() \ No newline at end of file diff --git a/Lecture 09/Python Code/AbstractFactory.py b/Lecture 09/Python Code/AbstractFactory.py new file mode 100644 index 0000000..4b72bc2 --- /dev/null +++ b/Lecture 09/Python Code/AbstractFactory.py @@ -0,0 +1,122 @@ +from abc import ABC, abstractmethod + +# --- Product 1 --> Burger --- +class Burger(ABC): + @abstractmethod + def prepare(self): + pass + +class BasicBurger(Burger): + def prepare(self): + print("Preparing Basic Burger with bun, patty, and ketchup!") + +class StandardBurger(Burger): + def prepare(self): + print("Preparing Standard Burger with bun, patty, cheese, and lettuce!") + +class PremiumBurger(Burger): + def prepare(self): + print("Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!") + +class BasicWheatBurger(Burger): + def prepare(self): + print("Preparing Basic Wheat Burger with bun, patty, and ketchup!") + +class StandardWheatBurger(Burger): + def prepare(self): + print("Preparing Standard Wheat Burger with bun, patty, cheese, and lettuce!") + +class PremiumWheatBurger(Burger): + def prepare(self): + print("Preparing Premium Wheat Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!") + +# --- Product 2 --> GarlicBread --- +class GarlicBread(ABC): + @abstractmethod + def prepare(self): + pass + +class BasicGarlicBread(GarlicBread): + def prepare(self): + print("Preparing Basic Garlic Bread with butter and garlic!") + +class CheeseGarlicBread(GarlicBread): + def prepare(self): + print("Preparing Cheese Garlic Bread with extra cheese and butter!") + +class BasicWheatGarlicBread(GarlicBread): + def prepare(self): + print("Preparing Basic Wheat Garlic Bread with butter and garlic!") + +class CheeseWheatGarlicBread(GarlicBread): + def prepare(self): + print("Preparing Cheese Wheat Garlic Bread with extra cheese and butter!") + +# --- Abstract Factory --- +class MealFactory(ABC): + @abstractmethod + def create_burger(self, type_): + pass + + @abstractmethod + def create_garlic_bread(self, type_): + pass + +# --- Concrete Factory 1 --- +class SinghBurger(MealFactory): + def create_burger(self, type_): + if type_.lower() == "basic": + return BasicBurger() + elif type_.lower() == "standard": + return StandardBurger() + elif type_.lower() == "premium": + return PremiumBurger() + else: + print("Invalid burger type!") + return None + + def create_garlic_bread(self, type_): + if type_.lower() == "basic": + return BasicGarlicBread() + elif type_.lower() == "cheese": + return CheeseGarlicBread() + else: + print("Invalid Garlic bread type!") + return None + +# --- Concrete Factory 2 --- +class KingBurger(MealFactory): + def create_burger(self, type_): + if type_.lower() == "basic": + return BasicWheatBurger() + elif type_.lower() == "standard": + return StandardWheatBurger() + elif type_.lower() == "premium": + return PremiumWheatBurger() + else: + print("Invalid burger type!") + return None + + def create_garlic_bread(self, type_): + if type_.lower() == "basic": + return BasicWheatGarlicBread() + elif type_.lower() == "cheese": + return CheeseWheatGarlicBread() + else: + print("Invalid Garlic bread type!") + return None + +# --- Main Section --- +if __name__ == "__main__": + burger_type = "basic" + garlic_bread_type = "cheese" + + meal_factory = SinghBurger() + + burger = meal_factory.create_burger(burger_type) + garlic_bread = meal_factory.create_garlic_bread(garlic_bread_type) + + if burger: + burger.prepare() + if garlic_bread: + garlic_bread.prepare() diff --git a/Lecture 09/Python Code/FactoryMethod.py b/Lecture 09/Python Code/FactoryMethod.py new file mode 100644 index 0000000..606c7c3 --- /dev/null +++ b/Lecture 09/Python Code/FactoryMethod.py @@ -0,0 +1,71 @@ +from abc import ABC, abstractmethod + +# Product Interface and subclasses +class Burger(ABC): + @abstractmethod + def prepare(self): + pass + +class BasicBurger(Burger): + def prepare(self): + print("Preparing Basic Burger with bun, patty, and ketchup!") + +class StandardBurger(Burger): + def prepare(self): + print("Preparing Standard Burger with bun, patty, cheese, and lettuce!") + +class PremiumBurger(Burger): + def prepare(self): + print("Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!") + +class BasicWheatBurger(Burger): + def prepare(self): + print("Preparing Basic Wheat Burger with bun, patty, and ketchup!") + +class StandardWheatBurger(Burger): + def prepare(self): + print("Preparing Standard Wheat Burger with bun, patty, cheese, and lettuce!") + +class PremiumWheatBurger(Burger): + def prepare(self): + print("Preparing Premium Wheat Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!") + +# Factory Interface and Concrete Factories +class BurgerFactory(ABC): + @abstractmethod + def create_burger(self, type_): + pass + +class SinghBurger(BurgerFactory): + def create_burger(self, type_): + if type_.lower() == "basic": + return BasicBurger() + elif type_.lower() == "standard": + return StandardBurger() + elif type_.lower() == "premium": + return PremiumBurger() + else: + print("Invalid burger type!") + return None + +class KingBurger(BurgerFactory): + def create_burger(self, type_): + if type_.lower() == "basic": + return BasicWheatBurger() + elif type_.lower() == "standard": + return StandardWheatBurger() + elif type_.lower() == "premium": + return PremiumWheatBurger() + else: + print("Invalid burger type!") + return None + +# Main Section +if __name__ == "__main__": + type_ = "basic" + + my_factory = SinghBurger() + burger = my_factory.create_burger(type_) + + if burger: + burger.prepare() diff --git a/Lecture 09/Python Code/SimpleFactory.py b/Lecture 09/Python Code/SimpleFactory.py new file mode 100644 index 0000000..df39f52 --- /dev/null +++ b/Lecture 09/Python Code/SimpleFactory.py @@ -0,0 +1,44 @@ +from abc import ABC, abstractmethod + +# --- Burger Interface --- +class Burger(ABC): + @abstractmethod + def prepare(self): + pass + +# --- Concrete Burger Implementations --- +class BasicBurger(Burger): + def prepare(self): + print("Preparing Basic Burger with bun, patty, and ketchup!") + +class StandardBurger(Burger): + def prepare(self): + print("Preparing Standard Burger with bun, patty, cheese, and lettuce!") + +class PremiumBurger(Burger): + def prepare(self): + print("Preparing Premium Burger with gourmet bun, premium patty, cheese, lettuce, and secret sauce!") + +# --- Burger Factory --- +class BurgerFactory: + def create_burger(self, type_): + if type_.lower() == "basic": + return BasicBurger() + elif type_.lower() == "standard": + return StandardBurger() + elif type_.lower() == "premium": + return PremiumBurger() + else: + print("Invalid burger type!") + return None + +# --- Main Section --- +if __name__ == "__main__": + type_ = "standard" + + my_burger_factory = BurgerFactory() + + burger = my_burger_factory.create_burger(type_) + + if burger: + burger.prepare() diff --git a/Lecture 10/Python Code/NoSingleton.py b/Lecture 10/Python Code/NoSingleton.py new file mode 100644 index 0000000..13c7350 --- /dev/null +++ b/Lecture 10/Python Code/NoSingleton.py @@ -0,0 +1,9 @@ +class NoSingleton: + def __init__(self): + print("Singleton Constructor called. New Object created.") + +if __name__ == "__main__": + s1 = NoSingleton() + s2 = NoSingleton() + + print(s1 is s2) diff --git a/Lecture 10/Python Code/SimpleSingleton.py b/Lecture 10/Python Code/SimpleSingleton.py new file mode 100644 index 0000000..3c305d5 --- /dev/null +++ b/Lecture 10/Python Code/SimpleSingleton.py @@ -0,0 +1,18 @@ +class SimpleSingleton: + _instance = None + + def __init__(self): + print("Singleton Constructor called") + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = SimpleSingleton() + return cls._instance + +if __name__ == "__main__": + s1 = SimpleSingleton.get_instance() + s2 = SimpleSingleton.get_instance() + + print(s1 is s2) + \ No newline at end of file diff --git a/Lecture 10/Python Code/ThreadSafeEagerSingleton.py b/Lecture 10/Python Code/ThreadSafeEagerSingleton.py new file mode 100644 index 0000000..2ad89ac --- /dev/null +++ b/Lecture 10/Python Code/ThreadSafeEagerSingleton.py @@ -0,0 +1,31 @@ +import threading + +class ThreadSafeLazySingleton: + _instance = None + _lock = threading.Lock() # A lock object to ensure thread safety + + def __init__(self): + if ThreadSafeLazySingleton._instance is not None: + raise RuntimeError("Use get_instance() instead") + print("Singleton Constructor Called!") + + @classmethod + def get_instance(cls): + if cls._instance is None: # First check (not locked) + with cls._lock: # Locking to ensure only one thread creates the instance + if cls._instance is None: # Second check (locked) + cls._instance = cls() + return cls._instance + +if __name__ == "__main__": + # Example usage + def create_singleton(): + singleton = ThreadSafeLazySingleton.get_instance() + print(f"Singleton instance: {singleton}") + + # Simulate multiple threads trying to access the singleton + threads = [threading.Thread(target=create_singleton) for _ in range(5)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/Main.py b/Lecture 11/Python Code/Tomato/Main.py new file mode 100644 index 0000000..3ad7c4f --- /dev/null +++ b/Lecture 11/Python Code/Tomato/Main.py @@ -0,0 +1,24 @@ +from TomatoApp import TomatoApp +from models.User import User + +if __name__ == "__main__": + # Simulating a happy flow + tomato = TomatoApp() + + # Simulate a user coming in + user = User(101, "Aditya", "Delhi") + print(f"User: {user.get_name()} is active.") + + # User searches for restaurants by location + restaurant_list = tomato.search_restaurants("Delhi") + + if not restaurant_list: + print("No restaurants found!") + else: + print("Found Restaurants:") + for restaurant in restaurant_list: + print(f" - {restaurant.get_name()}") + + # User selects a restaurant + tomato.select_restaurant(user, restaurant_list[0]) + print(f"Selected restaurant: {restaurant_list[0].get_name()}") \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/TomatoApp.py b/Lecture 11/Python Code/Tomato/TomatoApp.py new file mode 100644 index 0000000..406d75f --- /dev/null +++ b/Lecture 11/Python Code/Tomato/TomatoApp.py @@ -0,0 +1,85 @@ +from managers.RestaurantManager import RestaurantManager +from models.MenuItem import MenuItem +from models.Cart import Cart +from models.Order import Order +from models.User import User +from models.Restaurant import Restaurant +from factories.NowOrderFactory import NowOrderFactory +from factories.ScheduledOrderFactory import ScheduledOrderFactory +from services.NotificationService import NotificationService + + +class TomatoApp: + def __init__(self): + self.initialize_restaurants() + + def initialize_restaurants(self): + restaurant1 = Restaurant("Bikaner", "Delhi") + restaurant1.add_menu_item(MenuItem("P1", "Chole Bhature", 120)) + restaurant1.add_menu_item(MenuItem("P2", "Samosa", 15)) + + restaurant2 = Restaurant("Haldiram", "Kolkata") + restaurant2.add_menu_item(MenuItem("P1", "Raj Kachori", 80)) + restaurant2.add_menu_item(MenuItem("P2", "Pav Bhaji", 100)) + restaurant2.add_menu_item(MenuItem("P3", "Dhokla", 50)) + + restaurant3 = Restaurant("Saravana Bhavan", "Chennai") + restaurant3.add_menu_item(MenuItem("P1", "Masala Dosa", 90)) + restaurant3.add_menu_item(MenuItem("P2", "Idli Vada", 60)) + restaurant3.add_menu_item(MenuItem("P3", "Filter Coffee", 30)) + + restaurant_manager = RestaurantManager.get_instance() + restaurant_manager.add_restaurant(restaurant1) + restaurant_manager.add_restaurant(restaurant2) + restaurant_manager.add_restaurant(restaurant3) + + def search_restaurants(self, location): + return RestaurantManager.get_instance().search_by_location(location) + + def select_restaurant(self, user, restaurant): + cart = user.get_cart() + cart.set_restaurant(restaurant) + + def add_to_cart(self, user, item_code): + restaurant = user.get_cart().get_restaurant() + if restaurant is None: + print("Please select a restaurant first.") + return + for item in restaurant.get_menu(): + if item.get_code() == item_code: + user.get_cart().add_item(item) + break + + def checkout_now(self, user, order_type, payment_strategy): + return self.checkout(user, order_type, payment_strategy, NowOrderFactory()) + + def checkout_scheduled(self, user, order_type, payment_strategy, schedule_time): + return self.checkout(user, order_type, payment_strategy, ScheduledOrderFactory(schedule_time)) + + def checkout(self, user, order_type, payment_strategy, order_factory): + if user.get_cart().is_empty(): + return None + + user_cart = user.get_cart() + ordered_restaurant = user_cart.get_restaurant() + items_ordered = user_cart.get_items() + total_cost = user_cart.get_total_cost() + + order = order_factory.create_order(user, user_cart, ordered_restaurant, items_ordered, payment_strategy, total_cost, order_type) + RestaurantManager.get_instance().add_order(order) + return order + + def pay_for_order(self, user, order): + is_payment_success = order.process_payment() + + if is_payment_success: + NotificationService.notify(order) + user.get_cart().clear() + + def print_user_cart(self, user): + print("Items in cart:") + print("------------------------------------") + for item in user.get_cart().get_items(): + print(f"{item.get_code()} : {item.get_name()} : ₹{item.get_price()}") + print("------------------------------------") + print(f"Grand total : ₹{user.get_cart().get_total_cost()}") \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/__pycache__/TomatoApp.cpython-313.pyc b/Lecture 11/Python Code/Tomato/__pycache__/TomatoApp.cpython-313.pyc new file mode 100644 index 0000000..07489ce Binary files /dev/null and b/Lecture 11/Python Code/Tomato/__pycache__/TomatoApp.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/factories/NowOrderFactory.py b/Lecture 11/Python Code/Tomato/factories/NowOrderFactory.py new file mode 100644 index 0000000..ec42701 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/factories/NowOrderFactory.py @@ -0,0 +1,7 @@ +from factories.OrderFactory import OrderFactory +from models.Order import Order + +class NowOrderFactory(OrderFactory): + def create_order(self, user, cart): + order = Order(user, cart.get_restaurant(), cart.get_items(), "Now") + return order \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/factories/OrderFactory.py b/Lecture 11/Python Code/Tomato/factories/OrderFactory.py new file mode 100644 index 0000000..927fa53 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/factories/OrderFactory.py @@ -0,0 +1,6 @@ +from abc import ABC, abstractmethod + +class OrderFactory(ABC): + @abstractmethod + def create_order(self, user, cart): + pass \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/factories/ScheduledOrderFactory.py b/Lecture 11/Python Code/Tomato/factories/ScheduledOrderFactory.py new file mode 100644 index 0000000..4eff4eb --- /dev/null +++ b/Lecture 11/Python Code/Tomato/factories/ScheduledOrderFactory.py @@ -0,0 +1,7 @@ +from factories.OrderFactory import OrderFactory +from models.Order import Order + +class ScheduledOrderFactory(OrderFactory): + def create_order(self, user, cart, scheduled_time): + order = Order(user, cart.get_restaurant(), cart.get_items(), "Scheduled", scheduled_time) + return order \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/factories/__pycache__/NowOrderFactory.cpython-313.pyc b/Lecture 11/Python Code/Tomato/factories/__pycache__/NowOrderFactory.cpython-313.pyc new file mode 100644 index 0000000..0d9e0de Binary files /dev/null and b/Lecture 11/Python Code/Tomato/factories/__pycache__/NowOrderFactory.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/factories/__pycache__/OrderFactory.cpython-313.pyc b/Lecture 11/Python Code/Tomato/factories/__pycache__/OrderFactory.cpython-313.pyc new file mode 100644 index 0000000..b62b857 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/factories/__pycache__/OrderFactory.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/factories/__pycache__/ScheduledOrderFactory.cpython-313.pyc b/Lecture 11/Python Code/Tomato/factories/__pycache__/ScheduledOrderFactory.cpython-313.pyc new file mode 100644 index 0000000..a5771e9 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/factories/__pycache__/ScheduledOrderFactory.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/managers/OrderManager.py b/Lecture 11/Python Code/Tomato/managers/OrderManager.py new file mode 100644 index 0000000..cf8cd2c --- /dev/null +++ b/Lecture 11/Python Code/Tomato/managers/OrderManager.py @@ -0,0 +1,12 @@ +from services.NotificationService import NotificationService + +class OrderManager: + def __init__(self): + self.orders = [] + + def place_order(self, order): + self.orders.append(order) + NotificationService.notify(order) + + def get_orders(self): + return self.orders \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/managers/RestaurantManager.py b/Lecture 11/Python Code/Tomato/managers/RestaurantManager.py new file mode 100644 index 0000000..3ea0500 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/managers/RestaurantManager.py @@ -0,0 +1,18 @@ +class RestaurantManager: + _instance = None + + def __init__(self): + self.restaurants = [] + + @staticmethod + def get_instance(): + if RestaurantManager._instance is None: + RestaurantManager._instance = RestaurantManager() + return RestaurantManager._instance + + def add_restaurant(self, restaurant): + self.restaurants.append(restaurant) + + def search_by_location(self, location): + location = location.lower() + return [r for r in self.restaurants if r.get_location().lower() == location] \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/managers/__pycache__/RestaurantManager.cpython-313.pyc b/Lecture 11/Python Code/Tomato/managers/__pycache__/RestaurantManager.cpython-313.pyc new file mode 100644 index 0000000..2b754ad Binary files /dev/null and b/Lecture 11/Python Code/Tomato/managers/__pycache__/RestaurantManager.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/models/Cart.py b/Lecture 11/Python Code/Tomato/models/Cart.py new file mode 100644 index 0000000..cf60fa4 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/Cart.py @@ -0,0 +1,19 @@ +class Cart: + def __init__(self): + self.restaurant = None + self.items = [] + + def set_restaurant(self, restaurant): + self.restaurant = restaurant + + def get_restaurant(self): + return self.restaurant + + def add_item(self, item): + self.items.append(item) + + def get_items(self): + return self.items + + def calculate_total(self): + return sum(item.get_price() for item in self.items) \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/DeliveryOrder.py b/Lecture 11/Python Code/Tomato/models/DeliveryOrder.py new file mode 100644 index 0000000..cf991f9 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/DeliveryOrder.py @@ -0,0 +1,12 @@ +from models.Order import Order + +class DeliveryOrder(Order): + def __init__(self, user, restaurant, items, delivery_address, order_type="Delivery", scheduled_time=None): + super().__init__(user, restaurant, items, order_type, scheduled_time) + self.delivery_address = delivery_address + + def get_delivery_address(self): + return self.delivery_address + + def set_delivery_address(self, address): + self.delivery_address = address \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/MenuItem.py b/Lecture 11/Python Code/Tomato/models/MenuItem.py new file mode 100644 index 0000000..053aaa8 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/MenuItem.py @@ -0,0 +1,23 @@ +class MenuItem: + def __init__(self, code, name, price): + self.code = code + self.name = name + self.price = price + + def get_code(self): + return self.code + + def set_code(self, code): + self.code = code + + def get_name(self): + return self.name + + def set_name(self, name): + self.name = name + + def get_price(self): + return self.price + + def set_price(self, price): + self.price = price \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/Order.py b/Lecture 11/Python Code/Tomato/models/Order.py new file mode 100644 index 0000000..128de33 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/Order.py @@ -0,0 +1,32 @@ +class Order: + next_order_id = 1 + + def __init__(self, user, restaurant, items, order_type, scheduled_time=None): + self.order_id = Order.next_order_id + Order.next_order_id += 1 + self.user = user + self.restaurant = restaurant + self.items = items + self.order_type = order_type + self.scheduled_time = scheduled_time + + def get_order_id(self): + return self.order_id + + def get_user(self): + return self.user + + def get_restaurant(self): + return self.restaurant + + def get_items(self): + return self.items + + def get_total(self): + return sum(item.get_price() for item in self.items) + + def get_type(self): + return self.order_type + + def get_scheduled(self): + return self.scheduled_time \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/PickupOrder.py b/Lecture 11/Python Code/Tomato/models/PickupOrder.py new file mode 100644 index 0000000..43a028d --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/PickupOrder.py @@ -0,0 +1,12 @@ +from models.order import Order + +class PickupOrder(Order): + def __init__(self, user, restaurant, items, pickup_time, order_type="Pickup"): + super().__init__(user, restaurant, items, order_type, pickup_time) + self.pickup_time = pickup_time + + def get_pickup_time(self): + return self.pickup_time + + def set_pickup_time(self, time): + self.pickup_time = time \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/Restaurant.py b/Lecture 11/Python Code/Tomato/models/Restaurant.py new file mode 100644 index 0000000..e1949e7 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/Restaurant.py @@ -0,0 +1,27 @@ +class Restaurant: + next_restaurant_id = 0 + + def __init__(self, name, location): + self.restaurant_id = Restaurant.next_restaurant_id + 1 + Restaurant.next_restaurant_id += 1 + self.name = name + self.location = location + self.menu = [] + + def get_name(self): + return self.name + + def set_name(self, name): + self.name = name + + def get_location(self): + return self.location + + def set_location(self, location): + self.location = location + + def add_menu_item(self, item): + self.menu.append(item) + + def get_menu(self): + return self.menu \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/User.py b/Lecture 11/Python Code/Tomato/models/User.py new file mode 100644 index 0000000..5bbbc93 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/models/User.py @@ -0,0 +1,14 @@ +from models.Cart import Cart + +class User: + def __init__(self, user_id, name, location): + self.user_id = user_id + self.name = name + self.location = location + self.cart = Cart() + + def get_name(self): + return self.name + + def get_cart(self): + return self.cart \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/models/__pycache__/Cart.cpython-313.pyc b/Lecture 11/Python Code/Tomato/models/__pycache__/Cart.cpython-313.pyc new file mode 100644 index 0000000..10e42f3 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/models/__pycache__/Cart.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/models/__pycache__/MenuItem.cpython-313.pyc b/Lecture 11/Python Code/Tomato/models/__pycache__/MenuItem.cpython-313.pyc new file mode 100644 index 0000000..a6e7a13 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/models/__pycache__/MenuItem.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/models/__pycache__/Order.cpython-313.pyc b/Lecture 11/Python Code/Tomato/models/__pycache__/Order.cpython-313.pyc new file mode 100644 index 0000000..2872fc9 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/models/__pycache__/Order.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/models/__pycache__/Restaurant.cpython-313.pyc b/Lecture 11/Python Code/Tomato/models/__pycache__/Restaurant.cpython-313.pyc new file mode 100644 index 0000000..d02c2ea Binary files /dev/null and b/Lecture 11/Python Code/Tomato/models/__pycache__/Restaurant.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/models/__pycache__/User.cpython-313.pyc b/Lecture 11/Python Code/Tomato/models/__pycache__/User.cpython-313.pyc new file mode 100644 index 0000000..d44d0e0 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/models/__pycache__/User.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/services/NotificationService.py b/Lecture 11/Python Code/Tomato/services/NotificationService.py new file mode 100644 index 0000000..5d92b87 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/services/NotificationService.py @@ -0,0 +1,15 @@ +class NotificationService: + @staticmethod + def notify(order): + print(f"\nNotification: New {order.get_type()} order placed!") + print("---------------------------------------------") + print(f"Order ID: {order.get_order_id()}") + print(f"Customer: {order.get_user().get_name()}") + print(f"Restaurant: {order.get_restaurant().get_name()}") + print("Items Ordered:") + for item in order.get_items(): + print(f" - {item.get_name()} (₹{item.get_price()})") + print(f"Total: ₹{order.get_total()}") + print(f"Scheduled For: {order.get_scheduled()}") + print("Payment: Done") + print("---------------------------------------------") \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/services/__pycache__/NotificationService.cpython-313.pyc b/Lecture 11/Python Code/Tomato/services/__pycache__/NotificationService.cpython-313.pyc new file mode 100644 index 0000000..b3550e6 Binary files /dev/null and b/Lecture 11/Python Code/Tomato/services/__pycache__/NotificationService.cpython-313.pyc differ diff --git a/Lecture 11/Python Code/Tomato/strategies/CreditCardPaymentStrategy.py b/Lecture 11/Python Code/Tomato/strategies/CreditCardPaymentStrategy.py new file mode 100644 index 0000000..d54ca11 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/strategies/CreditCardPaymentStrategy.py @@ -0,0 +1,5 @@ +from strategies.PaymentStrategy import PaymentStrategy + +class CreditCardPaymentStrategy(PaymentStrategy): + def pay(self, amount): + print(f"Paid ₹{amount} using Credit Card.") \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/strategies/PaymentStrategy.py b/Lecture 11/Python Code/Tomato/strategies/PaymentStrategy.py new file mode 100644 index 0000000..bcf642f --- /dev/null +++ b/Lecture 11/Python Code/Tomato/strategies/PaymentStrategy.py @@ -0,0 +1,6 @@ +from abc import ABC, abstractmethod + +class PaymentStrategy(ABC): + @abstractmethod + def pay(self, amount): + pass \ No newline at end of file diff --git a/Lecture 11/Python Code/Tomato/strategies/UPIPaymentStrategy.py b/Lecture 11/Python Code/Tomato/strategies/UPIPaymentStrategy.py new file mode 100644 index 0000000..f212a21 --- /dev/null +++ b/Lecture 11/Python Code/Tomato/strategies/UPIPaymentStrategy.py @@ -0,0 +1,5 @@ +from strategies.PaymentStrategy import PaymentStrategy + +class UPIPaymentStrategy(PaymentStrategy): + def pay(self, amount): + print(f"Paid ₹{amount} using UPI.") diff --git a/Lecture 11/Python Code/Tomato/utils/TimeUtils.py b/Lecture 11/Python Code/Tomato/utils/TimeUtils.py new file mode 100644 index 0000000..0f846ea --- /dev/null +++ b/Lecture 11/Python Code/Tomato/utils/TimeUtils.py @@ -0,0 +1,6 @@ +from datetime import datetime + +class TimeUtils: + @staticmethod + def get_current_time(): + return datetime.now().strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/Lecture 12/Python Code/ObserverDesignPattern.py b/Lecture 12/Python Code/ObserverDesignPattern.py new file mode 100644 index 0000000..47247b3 --- /dev/null +++ b/Lecture 12/Python Code/ObserverDesignPattern.py @@ -0,0 +1,83 @@ +from abc import ABC, abstractmethod + + +# Observer interface: represents a subscriber +class ISubscriber(ABC): + @abstractmethod + def update(self): + pass + + +# Observable interface: represents a YouTube channel +class IChannel(ABC): + @abstractmethod + def subscribe(self, subscriber): + pass + + @abstractmethod + def unsubscribe(self, subscriber): + pass + + @abstractmethod + def notify_subscribers(self): + pass + + +# Concrete Subject: a YouTube channel that observers can subscribe to +class Channel(IChannel): + def __init__(self, name): + self.name = name + self.subscribers = [] + self.latest_video = None + + def subscribe(self, subscriber): + if subscriber not in self.subscribers: + self.subscribers.append(subscriber) + + def unsubscribe(self, subscriber): + if subscriber in self.subscribers: + self.subscribers.remove(subscriber) + + def notify_subscribers(self): + for subscriber in self.subscribers: + subscriber.update() + + def upload_video(self, title): + self.latest_video = title + print(f"\n[{self.name} uploaded \"{title}\"]") + self.notify_subscribers() + + def get_video_data(self): + return f"\nCheckout our new Video: {self.latest_video}\n" + + +# Concrete Observer: represents a subscriber to the channel +class Subscriber(ISubscriber): + def __init__(self, name, channel): + self.name = name + self.channel = channel + + def update(self): + print(f"Hey {self.name},{self.channel.get_video_data()}") + + +# Main function to demonstrate the Observer Design Pattern +if __name__ == "__main__": + # Create a channel and subscribers + channel = Channel("CoderArmy") + + subs1 = Subscriber("Varun", channel) + subs2 = Subscriber("Tarun", channel) + + # Varun and Tarun subscribe to CoderArmy + channel.subscribe(subs1) + channel.subscribe(subs2) + + # Upload a video: both Varun and Tarun are notified + channel.upload_video("Observer Pattern Tutorial") + + # Varun unsubscribes; Tarun remains subscribed + channel.unsubscribe(subs1) + + # Upload another video: only Tarun is notified + channel.upload_video("Decorator Pattern Tutorial") \ No newline at end of file