Skip to content

Commit 7f71d37

Browse files
Added type hints and pytest tests
1 parent 4fcbf1b commit 7f71d37

File tree

2 files changed

+103
-16
lines changed

2 files changed

+103
-16
lines changed

patterns/behavioral/strategy.py

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,77 @@
77
Enables selecting an algorithm at runtime.
88
"""
99

10+
from __future__ import annotations
11+
12+
from typing import Callable, Type
13+
14+
15+
class DiscountStrategyValidator: # Descriptor class for check perform
16+
def validate(self, obj: Order, value: Callable) -> bool:
17+
try:
18+
if obj.price - value(obj) < 0:
19+
raise ValueError(f"Discount cannot be applied due to negative price resulting. {value.__name__}")
20+
return True
21+
except ValueError as ex:
22+
print(str(ex))
23+
return False
24+
25+
def __set_name__(self, owner, name: str) -> None:
26+
self.private_name = '_' + name
27+
28+
def __set__(self, obj: Order, value: Callable = None) -> None:
29+
if value and self.validate(obj, value):
30+
setattr(obj, self.private_name, value)
31+
else:
32+
setattr(obj, self.private_name, None)
33+
34+
def __get__(self, obj: object, objtype: Type = None):
35+
return getattr(obj, self.private_name)
36+
1037

1138
class Order:
12-
def __init__(self, price, discount_strategy=None):
13-
self.price = price
39+
discount_strategy = DiscountStrategyValidator()
40+
41+
def __init__(self, price: float, discount_strategy: Callable = None) -> None:
42+
self.price: float = price
1443
self.discount_strategy = discount_strategy
1544

16-
def price_after_discount(self):
45+
def apply_discount(self) -> float:
1746
if self.discount_strategy:
1847
discount = self.discount_strategy(self)
1948
else:
2049
discount = 0
50+
2151
return self.price - discount
2252

23-
def __repr__(self):
24-
fmt = "<Price: {}, price after discount: {}>"
25-
return fmt.format(self.price, self.price_after_discount())
53+
def __repr__(self) -> str:
54+
return f"<Order price: {self.price} with discount strategy: {getattr(self.discount_strategy,'__name__',None)}>"
2655

2756

28-
def ten_percent_discount(order):
57+
def ten_percent_discount(order: Order) -> float:
2958
return order.price * 0.10
3059

3160

32-
def on_sale_discount(order):
61+
def on_sale_discount(order: Order) -> float:
3362
return order.price * 0.25 + 20
3463

3564

3665
def main():
3766
"""
38-
>>> Order(100)
39-
<Price: 100, price after discount: 100>
40-
41-
>>> Order(100, discount_strategy=ten_percent_discount)
42-
<Price: 100, price after discount: 90.0>
43-
44-
>>> Order(1000, discount_strategy=on_sale_discount)
45-
<Price: 1000, price after discount: 730.0>
67+
>>> order = Order(100, discount_strategy=ten_percent_discount)
68+
>>> print(order)
69+
<Order price: 100 with discount strategy: ten_percent_discount>
70+
>>> print(order.apply_discount())
71+
90.0
72+
>>> order = Order(100, discount_strategy=on_sale_discount)
73+
>>> print(order)
74+
<Order price: 100 with discount strategy: on_sale_discount>
75+
>>> print(order.apply_discount())
76+
55.0
77+
>>> order = Order(10, discount_strategy=on_sale_discount)
78+
Discount cannot be applied due to negative price resulting. on_sale_discount
79+
>>> print(order)
80+
<Order price: 10 with discount strategy: None>
4681
"""
4782

4883

tests/behavioral/test_strategy.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import pytest
2+
3+
from patterns.behavioral.strategy import Order, ten_percent_discount, on_sale_discount
4+
5+
6+
@pytest.fixture
7+
def order():
8+
return Order(100)
9+
10+
11+
@pytest.mark.parametrize(
12+
"func, discount",
13+
[
14+
(ten_percent_discount, 10.0),
15+
(on_sale_discount, 45.0)
16+
]
17+
)
18+
def test_discount_function_return(func, order, discount):
19+
assert func(order) == discount
20+
21+
22+
@pytest.mark.parametrize(
23+
"func, price",
24+
[
25+
(ten_percent_discount, 100),
26+
(on_sale_discount, 100)
27+
]
28+
)
29+
def test_order_discount_strategy_validate_success(func, price):
30+
order = Order(price, func)
31+
32+
assert order.price == price
33+
assert order.discount_strategy == func
34+
35+
36+
def test_order_discount_strategy_validate_error():
37+
order = Order(10, discount_strategy=on_sale_discount)
38+
39+
assert order.discount_strategy is None
40+
41+
42+
@pytest.mark.parametrize(
43+
"func, price, discount",
44+
[
45+
(ten_percent_discount, 100, 90.0),
46+
(on_sale_discount, 100, 55.0)
47+
]
48+
)
49+
def test_discount_apply_success(func, price, discount):
50+
order = Order(price, func)
51+
52+
assert order.apply_discount() == discount

0 commit comments

Comments
 (0)