1
1
![ Banner] ( https://raw.githubusercontent.com/Kantai235/php-design-pattern/master/DesignPatterns/Behavioral/StrategyPattern/Banner.png )
2
2
3
3
# 策略模式 Strategy Pattern
4
+ 策略模式,可以讓物件在運作時更改其行為或算法,你可以透過切換策略物件來改變計有的功能,你需要實作一個介面來代表這個策略物件,然後在主要類別當中去引入這個策略物件,在需要變更時來切換策略物件,來達成不同狀況下所需要的功能,就像是大頭菜的鈴錢有兩種模式,一種是原本的鈴錢,另一種則是過期後歸零,這個鈴錢運算的模式就可以抽離出來作為策略物件。
4
5
5
6
## UML
6
7
![ UML] ( https://raw.githubusercontent.com/Kantai235/php-design-pattern/master/DesignPatterns/Behavioral/StrategyPattern/UML.png )
7
8
8
9
## 實作
10
+ 首先我們要定義策略的介面,這個介面我們會希望策略物件必須要實作鈴錢運算(calculatePrice)的方法。
9
11
12
+ Strategy.php
13
+ ``` php
14
+ /**
15
+ * Interface Strategy.
16
+ */
17
+ interface Strategy
18
+ {
19
+ /**
20
+ * @return int
21
+ */
22
+ public function calculatePrice(int $price, int $count): int;
23
+ }
24
+ ```
25
+
26
+ 再來要實踐大頭菜的策略模式,首先是正常狀況下的大頭菜,會直接拿鈴錢價格、總數相成後即是鈴錢總價,並且將其回傳。
27
+
28
+ TurnipsStrategy.php
29
+ ``` php
30
+ /**
31
+ * Class TurnipsStrategy.
32
+ */
33
+ class TurnipsStrategy implements Strategy
34
+ {
35
+ /**
36
+ * @return int
37
+ */
38
+ public function calculatePrice(int $price, int $count): int
39
+ {
40
+ return $price * $count;
41
+ }
42
+ }
43
+ ```
44
+
45
+ 至於壞掉的部分,只要大頭菜壞掉就是賣不出去,所以不用進行任何運算,直接回傳 0 鈴錢即可。
46
+
47
+ SpoliedStrategy.php
48
+ ``` php
49
+ /**
50
+ * Class SpoliedStrategy.
51
+ */
52
+ class SpoliedStrategy implements Strategy
53
+ {
54
+ /**
55
+ * @return int
56
+ */
57
+ public function calculatePrice(int $price, int $count): int
58
+ {
59
+ return 0;
60
+ }
61
+ }
62
+ ```
63
+
64
+ 最後實作大頭菜物件,我們需要順便把策略物件丟進去,如果在建立大頭菜物件時沒有指定策略物件,那麼預設就給予正常的策略物件,並且提供一個可以臨時切換策略物件的方法,以及計算鈴錢總價的方法,這個計算的方法是透過呼叫策略物件的方法來實踐。
65
+
66
+ Turnips.php
67
+ ``` php
68
+ /**
69
+ * Class Turnips.
70
+ */
71
+ class Turnips
72
+ {
73
+ /**
74
+ * @var int
75
+ */
76
+ protected int $price;
77
+
78
+ /**
79
+ * @var int
80
+ */
81
+ protected int $count;
82
+
83
+ /**
84
+ * @var Strategy
85
+ */
86
+ protected Strategy $strategy;
87
+
88
+ /**
89
+ * Turnips constructor.
90
+ *
91
+ * @param int $price
92
+ * @param int $count
93
+ * @param Strategy $strategy
94
+ */
95
+ public function __construct(int $price, int $count, Strategy $strategy = null)
96
+ {
97
+ $this->price = $price;
98
+ $this->count = $count;
99
+
100
+ // 如果在建立大頭菜物件時沒有指定策略物件,那麼預設就給予正常的策略物件。
101
+ $this->strategy = empty($strategy) ? new TurnipsStrategy() : $strategy;
102
+ }
103
+
104
+ /**
105
+ * @param Strategy $strategy
106
+ */
107
+ public function setStrategy(Strategy $strategy)
108
+ {
109
+ $this->strategy = $strategy;
110
+ }
111
+
112
+ /**
113
+ * @return int
114
+ */
115
+ public function calculatePrice(): int
116
+ {
117
+ return $this->strategy->calculatePrice($this->price, $this->count);
118
+ }
119
+ }
120
+ ```
10
121
11
122
## 測試
123
+ 最後我們要測試策略大頭菜是否如預期的可以運行,我們接下來有一項測試分別是建立大頭菜物件,並且給予預設正常的策略物件,正常情況下可以計算出鈴錢,這時候把策略物件替換為壞掉的模式,再重複呼叫方法時,則是獲得 0 鈴錢。
12
124
125
+ StrategyPatternTest.php
126
+ ``` php
127
+ /**
128
+ * Class StrategyPatternTest.
129
+ */
130
+ class StrategyPatternTest extends TestCase
131
+ {
132
+ /**
133
+ * @test
134
+ */
135
+ public function test_strategy()
136
+ {
137
+ $turnips = new Turnips(100, 40, new TurnipsStrategy);
138
+ $this->assertEquals(4000, $turnips->calculatePrice());
139
+
140
+ $turnips->setStrategy(new SpoliedStrategy());
141
+ $this->assertEquals(0, $turnips->calculatePrice());
142
+ }
143
+ }
144
+ ```
13
145
14
146
最後測試的執行結果會獲得如下:
15
147
@@ -20,6 +152,16 @@ PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors.
20
152
PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
21
153
22
154
155
+ ==> ...fResponsibilitiesTest ✔ ✔ ✔
156
+ ==> CommandPatternTest ✔
157
+ ==> IteratorPatternTest ✔ ✔ ✔ ✔
158
+ ==> MediatorPatternTest ✔ ✔ ✔
159
+ ==> MementoPatternTest ✔
160
+ ==> NullObjectPatternTest ✔ ✔ ✔ ✔
161
+ ==> ObserverPatternTest ✔
162
+ ==> SpecificationPatternTest ✔ ✔ ✔ ✔
163
+ ==> StatePatternTest ✔
164
+ ==> StrategyPatternTest ✔
23
165
==> AbstractFactoryTest ✔ ✔ ✔ ✔
24
166
==> BuilderPatternTest ✔ ✔ ✔ ✔
25
167
==> FactoryMethodTest ✔ ✔ ✔ ✔
@@ -40,9 +182,9 @@ PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
40
182
==> ProxyPatternTest ✔ ✔
41
183
==> RegistryPatternTest ✔ ✔ ✔ ✔ ✔
42
184
43
- Time: 00:00.037 , Memory: 6 .00 MB
185
+ Time: 00:00.084 , Memory: 8 .00 MB
44
186
45
- OK (51 tests, 116 assertions)
187
+ OK (74 tests, 147 assertions)
46
188
```
47
189
48
190
## 完整程式碼
0 commit comments