Skip to content

Commit 74d363b

Browse files
authored
Merge pull request faif#257 from gyermolenko/refactor_strategy
Refactor Strategy
2 parents e0aaa5d + 422f69f commit 74d363b

File tree

2 files changed

+30
-53
lines changed

2 files changed

+30
-53
lines changed

behavioral/strategy.py

Lines changed: 29 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,48 @@
33

44
"""
55
*What is this pattern about?
6-
This pattern aims to encapsulate each algorithm and allow them to be
7-
interchangeable. Separating algorithms allows the client to scale
8-
with larger and more complex algorithms, since the client and the
9-
strategies are kept independent of each other.
10-
11-
Having the algorithms as an integral part of the client can cause the
12-
client to be larger and harder to maintain. This is more evident when
13-
supporting multiple algorithms. The separation of client and algorithm
14-
allows us to easily replace and vary the algorithm.
15-
16-
*What does this example do?
17-
Below the 'StrategyExample' is an example of the client while the two
18-
functions; 'execute_replacement1' and 'execute_replacement2' are
19-
examples of the implementation or strategy. In the example we can see
20-
that the client can vary it's 'execute' method by changing the
21-
strategy which is responsible for implementation.
22-
23-
http://stackoverflow.com/questions/963965/how-is-this-strategy-pattern
24-
-written-in-python-the-sample-in-wikipedia
25-
In most of other languages Strategy pattern is implemented via creating some
26-
base strategy interface/abstract class and subclassing it with a number of
27-
concrete strategies (as we can see at
28-
http://en.wikipedia.org/wiki/Strategy_pattern), however Python supports
29-
higher-order functions and allows us to have only one class and inject
30-
functions into it's instances, as shown in this example.
6+
Define a family of algorithms, encapsulate each one, and make them interchangeable.
7+
Strategy lets the algorithm vary independently from clients that use it.
318
329
*TL;DR80
3310
Enables selecting an algorithm at runtime.
3411
"""
3512

36-
import types
37-
38-
39-
class StrategyExample:
40-
def __init__(self, func=None):
41-
self.name = 'Strategy Example 0'
42-
if func is not None:
43-
self.execute = types.MethodType(func, self)
44-
45-
def execute(self):
46-
print(self.name)
4713

14+
class Order:
15+
def __init__(self, price, discount_strategy=None):
16+
self.price = price
17+
self.discount_strategy = discount_strategy
4818

49-
def execute_replacement1(self):
50-
print(self.name + ' from execute 1')
19+
def price_after_discount(self):
20+
if self.discount_strategy:
21+
discount = self.discount_strategy(self)
22+
else:
23+
discount = 0
24+
return self.price - discount
5125

26+
def __repr__(self):
27+
fmt = "<Price: {}, price after discount: {}>"
28+
return fmt.format(self.price, self.price_after_discount())
5229

53-
def execute_replacement2(self):
54-
print(self.name + ' from execute 2')
5530

31+
def ten_percent_discount(order):
32+
return order.price * 0.10
5633

57-
if __name__ == '__main__':
58-
strat0 = StrategyExample()
5934

60-
strat1 = StrategyExample(execute_replacement1)
61-
strat1.name = 'Strategy Example 1'
35+
def on_sale_discount(order):
36+
return order.price * 0.25 + 20
6237

63-
strat2 = StrategyExample(execute_replacement2)
64-
strat2.name = 'Strategy Example 2'
6538

66-
strat0.execute()
67-
strat1.execute()
68-
strat2.execute()
39+
if __name__ == "__main__":
40+
order0 = Order(100)
41+
order1 = Order(100, discount_strategy=ten_percent_discount)
42+
order2 = Order(1000, discount_strategy=on_sale_discount)
43+
print(order0)
44+
print(order1)
45+
print(order2)
6946

7047
### OUTPUT ###
71-
# Strategy Example 0
72-
# Strategy Example 1 from execute 1
73-
# Strategy Example 2 from execute 2
48+
# <Price: 100, price after discount: 100>
49+
# <Price: 100, price after discount: 90.0>
50+
# <Price: 1000, price after discount: 730.0>

tests/test_strategy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def test_print_output(self):
1414
"""
1515
output = subprocess.check_output(["python", "behavioral/strategy.py"])
1616
expected_output = os.linesep.join(
17-
['Strategy Example 0', 'Strategy Example 1 from execute 1', 'Strategy Example 2 from execute 2', '']
17+
['<Price: 100, price after discount: 100>', '<Price: 100, price after discount: 90.0>', '<Price: 1000, price after discount: 730.0>', '']
1818
)
1919
# byte representation required due to EOF returned subprocess
2020
expected_output_as_bytes = expected_output.encode(encoding='UTF-8')

0 commit comments

Comments
 (0)