diff --git a/patterns/behavioral/strategy.py b/patterns/behavioral/strategy.py index 92d11f255..595df255e 100644 --- a/patterns/behavioral/strategy.py +++ b/patterns/behavioral/strategy.py @@ -8,41 +8,81 @@ """ +from __future__ import annotations + +from typing import Callable, Type + + +class DiscountStrategyValidator: # Descriptor class for check perform + @staticmethod + def validate(obj: Order, value: Callable) -> bool: + try: + if obj.price - value(obj) < 0: + raise ValueError( + f"Discount cannot be applied due to negative price resulting. {value.__name__}" + ) + except ValueError as ex: + print(str(ex)) + return False + else: + return True + + def __set_name__(self, owner, name: str) -> None: + self.private_name = f"_{name}" + + def __set__(self, obj: Order, value: Callable = None) -> None: + if value and self.validate(obj, value): + setattr(obj, self.private_name, value) + else: + setattr(obj, self.private_name, None) + + def __get__(self, obj: object, objtype: Type = None): + return getattr(obj, self.private_name) + + class Order: - def __init__(self, price, discount_strategy=None): - self.price = price + discount_strategy = DiscountStrategyValidator() + + def __init__(self, price: float, discount_strategy: Callable = None) -> None: + self.price: float = price self.discount_strategy = discount_strategy - def price_after_discount(self): + def apply_discount(self) -> float: if self.discount_strategy: discount = self.discount_strategy(self) else: discount = 0 + return self.price - discount - def __repr__(self): - fmt = "" - return fmt.format(self.price, self.price_after_discount()) + def __repr__(self) -> str: + return f"" -def ten_percent_discount(order): +def ten_percent_discount(order: Order) -> float: return order.price * 0.10 -def on_sale_discount(order): +def on_sale_discount(order: Order) -> float: return order.price * 0.25 + 20 def main(): """ - >>> Order(100) - - - >>> Order(100, discount_strategy=ten_percent_discount) - - - >>> Order(1000, discount_strategy=on_sale_discount) - + >>> order = Order(100, discount_strategy=ten_percent_discount) + >>> print(order) + + >>> print(order.apply_discount()) + 90.0 + >>> order = Order(100, discount_strategy=on_sale_discount) + >>> print(order) + + >>> print(order.apply_discount()) + 55.0 + >>> order = Order(10, discount_strategy=on_sale_discount) + Discount cannot be applied due to negative price resulting. on_sale_discount + >>> print(order) + """ diff --git a/patterns/structural/3-tier.py b/patterns/structural/3-tier.py index 64835f997..ecc042430 100644 --- a/patterns/structural/3-tier.py +++ b/patterns/structural/3-tier.py @@ -7,7 +7,7 @@ class Data: - """ Data Store Class """ + """Data Store Class""" products = { "milk": {"price": 1.50, "quantity": 10}, @@ -22,7 +22,7 @@ def __get__(self, obj, klas): class BusinessLogic: - """ Business logic holding data store instances """ + """Business logic holding data store instances""" data = Data() @@ -36,7 +36,7 @@ def product_information( class Ui: - """ UI interaction class """ + """UI interaction class""" def __init__(self) -> None: self.business_logic = BusinessLogic() diff --git a/patterns/structural/front_controller.py b/patterns/structural/front_controller.py index 9377fefeb..d93f74d6e 100644 --- a/patterns/structural/front_controller.py +++ b/patterns/structural/front_controller.py @@ -31,7 +31,7 @@ def dispatch(self, request): class RequestController: - """ front controller """ + """front controller""" def __init__(self): self.dispatcher = Dispatcher() @@ -44,7 +44,7 @@ def dispatch_request(self, request): class Request: - """ request """ + """request""" mobile_type = "mobile" tablet_type = "tablet" diff --git a/tests/behavioral/test_strategy.py b/tests/behavioral/test_strategy.py new file mode 100644 index 000000000..6a3b25041 --- /dev/null +++ b/tests/behavioral/test_strategy.py @@ -0,0 +1,52 @@ +import pytest + +from patterns.behavioral.strategy import Order, ten_percent_discount, on_sale_discount + + +@pytest.fixture +def order(): + return Order(100) + + +@pytest.mark.parametrize( + "func, discount", + [ + (ten_percent_discount, 10.0), + (on_sale_discount, 45.0) + ] +) +def test_discount_function_return(func, order, discount): + assert func(order) == discount + + +@pytest.mark.parametrize( + "func, price", + [ + (ten_percent_discount, 100), + (on_sale_discount, 100) + ] +) +def test_order_discount_strategy_validate_success(func, price): + order = Order(price, func) + + assert order.price == price + assert order.discount_strategy == func + + +def test_order_discount_strategy_validate_error(): + order = Order(10, discount_strategy=on_sale_discount) + + assert order.discount_strategy is None + + +@pytest.mark.parametrize( + "func, price, discount", + [ + (ten_percent_discount, 100, 90.0), + (on_sale_discount, 100, 55.0) + ] +) +def test_discount_apply_success(func, price, discount): + order = Order(price, func) + + assert order.apply_discount() == discount