Skip to content

Commit 7c69b1b

Browse files
committedOct 8, 2020
新增 策略模式 Strategy Pattern
1 parent 95b7260 commit 7c69b1b

File tree

9 files changed

+1639
-3
lines changed

9 files changed

+1639
-3
lines changed
 

‎DesignPatterns/Behavioral/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
狀態模式,讓物件的狀態改變時,一同改變物件的行為模式,就像是大頭菜(Turnips)這個物件,有沒有壞掉只是一個狀態(State)來辨別,但如果壞掉了,那麼會因為狀態改變的關係,而讓大頭菜計算鈴錢價格的方式也跟著改變。
2929

3030
## [策略模式 Strategy Pattern](/DesignPatterns/Behavioral/StrategyPattern)
31-
31+
策略模式,可以讓物件在運作時更改其行為或算法,你可以透過切換策略物件來改變計有的功能,你需要實作一個介面來代表這個策略物件,然後在主要類別當中去引入這個策略物件,在需要變更時來切換策略物件,來達成不同狀況下所需要的功能,就像是大頭菜的鈴錢有兩種模式,一種是原本的鈴錢,另一種則是過期後歸零,這個鈴錢運算的模式就可以抽離出來作為策略物件。
3232

3333
## [模板方法 Template Method](/DesignPatterns/Behavioral/TemplateMethod)
3434

‎DesignPatterns/Behavioral/StrategyPattern/README.md

+144-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,147 @@
11
![Banner](https://raw.githubusercontent.com/Kantai235/php-design-pattern/master/DesignPatterns/Behavioral/StrategyPattern/Banner.png)
22

33
# 策略模式 Strategy Pattern
4+
策略模式,可以讓物件在運作時更改其行為或算法,你可以透過切換策略物件來改變計有的功能,你需要實作一個介面來代表這個策略物件,然後在主要類別當中去引入這個策略物件,在需要變更時來切換策略物件,來達成不同狀況下所需要的功能,就像是大頭菜的鈴錢有兩種模式,一種是原本的鈴錢,另一種則是過期後歸零,這個鈴錢運算的模式就可以抽離出來作為策略物件。
45

56
## UML
67
![UML](https://raw.githubusercontent.com/Kantai235/php-design-pattern/master/DesignPatterns/Behavioral/StrategyPattern/UML.png)
78

89
## 實作
10+
首先我們要定義策略的介面,這個介面我們會希望策略物件必須要實作鈴錢運算(calculatePrice)的方法。
911

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+
```
10121

11122
## 測試
123+
最後我們要測試策略大頭菜是否如預期的可以運行,我們接下來有一項測試分別是建立大頭菜物件,並且給予預設正常的策略物件,正常情況下可以計算出鈴錢,這時候把策略物件替換為壞掉的模式,再重複呼叫方法時,則是獲得 0 鈴錢。
12124

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+
```
13145

14146
最後測試的執行結果會獲得如下:
15147

@@ -20,6 +152,16 @@ PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors.
20152
PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
21153
22154
155+
==> ...fResponsibilitiesTest ✔ ✔ ✔
156+
==> CommandPatternTest ✔
157+
==> IteratorPatternTest ✔ ✔ ✔ ✔
158+
==> MediatorPatternTest ✔ ✔ ✔
159+
==> MementoPatternTest ✔
160+
==> NullObjectPatternTest ✔ ✔ ✔ ✔
161+
==> ObserverPatternTest ✔
162+
==> SpecificationPatternTest ✔ ✔ ✔ ✔
163+
==> StatePatternTest ✔
164+
==> StrategyPatternTest ✔
23165
==> AbstractFactoryTest ✔ ✔ ✔ ✔
24166
==> BuilderPatternTest ✔ ✔ ✔ ✔
25167
==> FactoryMethodTest ✔ ✔ ✔ ✔
@@ -40,9 +182,9 @@ PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
40182
==> ProxyPatternTest ✔ ✔
41183
==> RegistryPatternTest ✔ ✔ ✔ ✔ ✔
42184
43-
Time: 00:00.037, Memory: 6.00 MB
185+
Time: 00:00.084, Memory: 8.00 MB
44186
45-
OK (51 tests, 116 assertions)
187+
OK (74 tests, 147 assertions)
46188
```
47189

48190
## 完整程式碼
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\StrategyPattern;
4+
5+
/**
6+
* Class SpoliedStrategy.
7+
*/
8+
class SpoliedStrategy implements Strategy
9+
{
10+
/**
11+
* @return int
12+
*/
13+
public function calculatePrice(int $price, int $count): int
14+
{
15+
return 0;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\StrategyPattern;
4+
5+
/**
6+
* Interface Strategy.
7+
*/
8+
interface Strategy
9+
{
10+
/**
11+
* @return int
12+
*/
13+
public function calculatePrice(int $price, int $count): int;
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\StrategyPattern;
4+
5+
/**
6+
* Class Turnips.
7+
*/
8+
class Turnips
9+
{
10+
/**
11+
* @var int
12+
*/
13+
protected int $price;
14+
15+
/**
16+
* @var int
17+
*/
18+
protected int $count;
19+
20+
/**
21+
* @var Strategy
22+
*/
23+
protected Strategy $strategy;
24+
25+
/**
26+
* Turnips constructor.
27+
*
28+
* @param int $price
29+
* @param int $count
30+
* @param Strategy $strategy
31+
*/
32+
public function __construct(int $price, int $count, Strategy $strategy = null)
33+
{
34+
$this->price = $price;
35+
$this->count = $count;
36+
$this->strategy = empty($strategy) ? new TurnipsStrategy() : $strategy;
37+
}
38+
39+
/**
40+
* @param Strategy $strategy
41+
*/
42+
public function setStrategy(Strategy $strategy)
43+
{
44+
$this->strategy = $strategy;
45+
}
46+
47+
/**
48+
* @return int
49+
*/
50+
public function calculatePrice(): int
51+
{
52+
return $this->strategy->calculatePrice($this->price, $this->count);
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\StrategyPattern;
4+
5+
/**
6+
* Class TurnipsStrategy.
7+
*/
8+
class TurnipsStrategy implements Strategy
9+
{
10+
/**
11+
* @return int
12+
*/
13+
public function calculatePrice(int $price, int $count): int
14+
{
15+
return $price * $count;
16+
}
17+
}
Loading
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Tests\Behavioral;
4+
5+
use DesignPatterns\Behavioral\StrategyPattern\SpoliedStrategy;
6+
use DesignPatterns\Behavioral\StrategyPattern\Turnips;
7+
use DesignPatterns\Behavioral\StrategyPattern\TurnipsStrategy;
8+
use PHPUnit\Framework\TestCase;
9+
10+
/**
11+
* Class StrategyPatternTest.
12+
*/
13+
class StrategyPatternTest extends TestCase
14+
{
15+
/**
16+
* @test
17+
*/
18+
public function test_strategy()
19+
{
20+
$turnips = new Turnips(100, 40, new TurnipsStrategy);
21+
$this->assertEquals(4000, $turnips->calculatePrice());
22+
23+
$turnips->setStrategy(new SpoliedStrategy());
24+
$this->assertEquals(0, $turnips->calculatePrice());
25+
}
26+
}

0 commit comments

Comments
 (0)
Please sign in to comment.