From 6343019850381b51c4f13d94b182a3a7a9345e78 Mon Sep 17 00:00:00 2001 From: Filippo Buoncompagni Date: Fri, 22 Dec 2023 16:18:38 +0100 Subject: [PATCH 1/2] Practice 1 and 2 --- 1 - coupling and cohesion/model_brands.py | 19 +++++++ 1 - coupling and cohesion/my_corrections.py | 60 +++++++++++++++++++++ 2 - dependency inversion/my_corrections.py | 46 ++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 1 - coupling and cohesion/model_brands.py create mode 100644 1 - coupling and cohesion/my_corrections.py create mode 100644 2 - dependency inversion/my_corrections.py diff --git a/1 - coupling and cohesion/model_brands.py b/1 - coupling and cohesion/model_brands.py new file mode 100644 index 0000000..e142716 --- /dev/null +++ b/1 - coupling and cohesion/model_brands.py @@ -0,0 +1,19 @@ +from abc import ABC, abstractmethod + + +class Brand(ABC): + @abstractmethod + def catalogue_price(self): + pass + +class TeslaModel3Brand(Brand): + def catalogue_price(self): + return 60000 + +class VolkswagenID3Brand(Brand): + def catalogue_price(self): + return 35000 + +class BMW5Brand(Brand): + def catalogue_price(self): + return 45000 \ No newline at end of file diff --git a/1 - coupling and cohesion/my_corrections.py b/1 - coupling and cohesion/my_corrections.py new file mode 100644 index 0000000..977c196 --- /dev/null +++ b/1 - coupling and cohesion/my_corrections.py @@ -0,0 +1,60 @@ +import string +import random +from abc import ABC, abstractmethod +from model_brands import Brand, TeslaModel3Brand, VolkswagenID3Brand, BMW5Brand + +class BaseRegistry(ABC): + @abstractmethod + def generate_vehicle_id(self, *args, **kwargs): + pass + + @abstractmethod + def generate_vehicle_license(self, *args, **kwargs): + pass + +class VehicleRegistry(BaseRegistry): + + def generate_vehicle_id(self, length): + return ''.join(random.choices(string.ascii_uppercase, k=length)) + + def generate_vehicle_license(self, id): + return f"{id[:2]}-{''.join(random.choices(string.digits, k=2))}-{''.join(random.choices(string.ascii_uppercase, k=2))}" + +class Application: + + def register_vehicle(self, registry: BaseRegistry): + + # generate a vehicle id of length 12 + self.vehicle_id = registry.generate_vehicle_id(12) + + # now generate a license plate for the vehicle + # using the first two characters of the vehicle id + self.license_plate = registry.generate_vehicle_license(self.vehicle_id) + + @staticmethod + def compute_catalog_price(brand: Brand): + return brand.catalogue_price() + + @staticmethod + def compute_tax_percentage(brand: Brand): + # compute the tax percentage (default 5% of the catalogue price, except for electric cars where it is 2%) + tax_percentage = 0.05 + if brand == "Tesla Model 3" or brand == "Volkswagen ID3": + tax_percentage = 0.02 + + # compute the payable tax + payable_tax = tax_percentage * Application.compute_catalog_price(brand) + return payable_tax + + def get_info_vehicle_registration(self, registry: BaseRegistry, brand: Brand): + # print out the vehicle registration information + self.register_vehicle(registry) + print("Registration complete. Vehicle information:") + print(f"Brand: {brand}") + print(f"Id: {self.vehicle_id}") + print(f"License plate: {self.license_plate}") + print(f"Payable tax: {self.compute_tax_percentage(brand)}") + +app = Application() +registry = VehicleRegistry() +app.get_info_vehicle_registration(registry, TeslaModel3Brand()) diff --git a/2 - dependency inversion/my_corrections.py b/2 - dependency inversion/my_corrections.py new file mode 100644 index 0000000..bf0ec40 --- /dev/null +++ b/2 - dependency inversion/my_corrections.py @@ -0,0 +1,46 @@ +from abc import ABC, abstractmethod + +# Dependency inversion helps reduce coupling in your code +# Uses abstractions to separate classes +class Switchable(ABC): + @abstractmethod + def turn_on(self): + pass + @abstractmethod + def turn_off(self): + pass + +class LightBulb(Switchable): + def turn_on(self): + print("LightBulb: turned on...") + + def turn_off(self): + print("LightBulb: turned off...") + +class Fan(Switchable): + def turn_on(self): + print("LightBulb: turned on...") + + def turn_off(self): + print("LightBulb: turned off...") + +class ElectricPowerSwitch: + + def __init__(self, client: Switchable): + self.client = client + self.on = False + + def press(self): + if self.on: + self.client.turn_off() + self.on = False + else: + self.client.turn_on() + self.on = True + + +light_bulb = LightBulb() +fan = Fan() +switch = ElectricPowerSwitch(fan) +switch.press() +switch.press() \ No newline at end of file From ffc48977f178e1518ed24b4fb091faf7c466df7e Mon Sep 17 00:00:00 2001 From: Filippo Buoncompagni Date: Wed, 3 Jan 2024 15:25:16 +0100 Subject: [PATCH 2/2] Observer, bridge and template corrections --- 3 - strategy pattern/my_corrections.py | 65 ++++++++++++++++++ 4 - observer pattern/my_observer.py | 60 +++++++++++++++++ 6 - template method & bridge/my_corrections | 67 +++++++++++++++++++ 6 - template method & bridge/trading-after.py | 2 +- 4 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 3 - strategy pattern/my_corrections.py create mode 100644 4 - observer pattern/my_observer.py create mode 100644 6 - template method & bridge/my_corrections diff --git a/3 - strategy pattern/my_corrections.py b/3 - strategy pattern/my_corrections.py new file mode 100644 index 0000000..4a5d446 --- /dev/null +++ b/3 - strategy pattern/my_corrections.py @@ -0,0 +1,65 @@ +import string +import random +from typing import List +from abc import ABC, abstractmethod + +def generate_id(length=8): + # helper function for generating an id + return ''.join(random.choices(string.ascii_uppercase, k=length)) + + +class SupportTicket: + + def __init__(self, customer, issue): + self.id = generate_id() + self.customer = customer + self.issue = issue + +# Strategy pattern improve cohesion. +class BaseProcessingStrategy(ABC): + @abstractmethod + def create_ordering(self, tickets: List[SupportTicket]) -> List[SupportTicket]: + pass + +class FIFOProcessingStrategy(BaseProcessingStrategy): + def create_ordering(self, tickets: List[SupportTicket]) -> List[SupportTicket]: + return tickets.copy() + +class FILOProcessingStrategy(BaseProcessingStrategy): + def create_ordering(self, tickets: List[SupportTicket]) -> List[SupportTicket]: + tickets_copy = tickets.copy() + return tickets_copy.reverse() + + +class CustomerSupport: + + def __init__(self): + self.tickets = [] + + + def create_ticket(self, customer, issue): + self.tickets.append(SupportTicket(customer, issue)) + + def process_tickets(self, processing_strategy: BaseProcessingStrategy): + tickets_list = processing_strategy.create_ordering(self.tickets) + for ticket in tickets_list: + self.process_ticket(ticket) + + def process_ticket(self, ticket: SupportTicket): + print("==================================") + print(f"Processing ticket id: {ticket.id}") + print(f"Customer: {ticket.customer}") + print(f"Issue: {ticket.issue}") + print("==================================") + + +# create the application +app = CustomerSupport() + +# register a few tickets +app.create_ticket("John Smith", "My computer makes strange sounds!") +app.create_ticket("Linus Sebastian", "I can't upload any videos, please help.") +app.create_ticket("Arjan Egges", "VSCode doesn't automatically solve my bugs.") + +# process the tickets +app.process_tickets(FIFOProcessingStrategy()) diff --git a/4 - observer pattern/my_observer.py b/4 - observer pattern/my_observer.py new file mode 100644 index 0000000..f257bac --- /dev/null +++ b/4 - observer pattern/my_observer.py @@ -0,0 +1,60 @@ +from abc import ABC, abstractmethod +import random + +class Observable(ABC): + @abstractmethod + def add(self, *args, **kwargs): + pass + + @abstractmethod + def remove(self, *args, **kwargs): + pass + + @abstractmethod + def notify(self): + pass + +class Observer(ABC): + @abstractmethod + def update(self): + pass + +class WeatherStation: + def __init__(self): + self.observers = [] + + def add(self, observer: Observer): + self.observers.append(observer) + + def remove(self, observer: Observer): + self.observers.remove(observer) + + def notify(self): + for observer in self.observers: + observer.update() + + def get_temperature(self): + temperature = random.randint(0, 30) + return temperature + +class Phone: + def __init__(self, observable: Observable) -> None: + self.observable = observable + + def update(self): + print(f"New temperature detected {self.observable.get_temperature()}") + +class WindowDisplay: + def __init__(self, observable: Observable) -> None: + self.observable = observable + + def update(self): + print(f"New temperature detected {self.observable.get_temperature()}") + + +station = WeatherStation() +phone = Phone(station) +window = WindowDisplay(station) +station.add(phone) +station.add(window) +station.notify() \ No newline at end of file diff --git a/6 - template method & bridge/my_corrections b/6 - template method & bridge/my_corrections new file mode 100644 index 0000000..41671f8 --- /dev/null +++ b/6 - template method & bridge/my_corrections @@ -0,0 +1,67 @@ +from typing import List +from abc import ABC, abstractmethod + +class Exchange(ABC): + @abstractmethod + def connect(self): + pass + + @abstractmethod + def get_market_data(self, coin: str) -> List[float]: + pass + +class BinanceExchange(Exchange): + def connect(self): + print("Connecting to Binance...") + + def get_market_data(self, coin: str) -> List[float]: + return [10, 12, 14, 18] + +class TraderBot(ABC): # this is my template -> should_buy and should_sell shoul be implemented in the concretion. + def __init__(self, exchange: Exchange) -> None: + self.exchange = exchange + + @abstractmethod + def should_buy(self, prices: List[float]) -> bool: + pass + + @abstractmethod + def should_sell(self, prices: List[float]) -> bool: + pass + + def check_prices(self, coin: str): + self.exchange.connect() + prices = self.exchange.get_market_data(coin) + should_buy = self.should_buy(prices) + should_sell = self.should_sell(prices) + if should_buy: + print(f"You should buy {coin}!") + elif should_sell: + print(f"You should sell {coin}!") + else: + print(f"No action needed for {coin}.") + +class MinMaxTrader(TraderBot): + + def should_buy(self, prices: List[float]) -> bool: + return prices[-1] == min(prices) + + def should_sell(self, prices: List[float]) -> bool: + return prices[-1] == max(prices) + +class AverageTrader(TraderBot): + + def should_buy(self, prices: List[float]) -> bool: + return prices[-1] < self.list_average(prices) + + def should_sell(self, prices: List[float]) -> bool: + return prices[-1] > self.list_average(prices) + + def list_average(self, l: List[float]) -> float: + return sum(l) / len(l) + + + + +application = MinMaxTrader(BinanceExchange()) +application.check_prices("BTC/USD") diff --git a/6 - template method & bridge/trading-after.py b/6 - template method & bridge/trading-after.py index d9ecd54..a7ad588 100644 --- a/6 - template method & bridge/trading-after.py +++ b/6 - template method & bridge/trading-after.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import List -class TradingBot(ABC): +class TradingBot(ABC): # The template def connect(self): print(f"Connecting to Crypto exchange...")