Skip to content

Commit 506ddc3

Browse files
committed
新增 規格模式 Specification Pattern
1 parent 705139a commit 506ddc3

File tree

9 files changed

+1821
-3
lines changed

9 files changed

+1821
-3
lines changed

DesignPatterns/Behavioral/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
觀察者模式,一種現在全中國都知道你來了的模式,就有點像是收音機,打開收音機就開始自動接收廣播,關掉收音機就停止接收,就有點像是動森的連線模式,你跟朋友在同一座島遊玩時,如果有其他朋友來玩,那你們通通都會收到這個通知,然後開始看渡渡鳥航空飛起來的動畫。
2323

2424
## [規格模式 Specification Pattern](/DesignPatterns/Behavioral/SpecificationPattern)
25-
25+
規格模式,將邏輯條件給抽離出來,獨立成一個模組,而不是在物件內透過邏輯判斷來撰寫複雜的程式碼,簡化物件所需要實踐的邏輯,物件可以套用一個規則,也可以套用多種規則,就像大頭菜本身的價格運算是一種規格,過期後的價格運算又是另一種規格,可以把這個價格運算的邏輯抽離出來獨立成模組。
2626

2727
## [狀態模式 State Pattern](/DesignPatterns/Behavioral/StatePattern)
2828

DesignPatterns/Behavioral/SpecificationPattern/README.md

Lines changed: 209 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,214 @@
11
![Banner](https://raw.githubusercontent.com/Kantai235/php-design-pattern/master/DesignPatterns/Behavioral/SpecificationPattern/Banner.png)
22

33
# 規格模式 Specification Pattern
4+
規格模式,將邏輯條件給抽離出來,獨立成一個模組,而不是在物件內透過邏輯判斷來撰寫複雜的程式碼,簡化物件所需要實踐的邏輯,物件可以套用一個規則,也可以套用多種規則,就像大頭菜本身的價格運算是一種規格,過期後的價格運算又是另一種規格,可以把這個價格運算的邏輯抽離出來獨立成模組。
45

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

89
## 實作
10+
首當其中我們需要把大頭菜物件給建立出來,具有價格(price)以及數量(count)的記錄、讀取功能,原本會提供計算鈴錢總計(calculatePrice)的功能,但這部分是運算邏輯,所以我們需要把這個功能抽離出來放到規格模組(Specification)當中。
911

12+
Turnips.php
13+
```php
14+
/**
15+
* Class Turnips.
16+
*/
17+
class Turnips
18+
{
19+
/**
20+
* @var int
21+
*/
22+
protected int $price = 0;
23+
24+
/**
25+
* @var int
26+
*/
27+
protected int $count = 0;
28+
29+
/**
30+
* Turnips constructor.
31+
*
32+
* @param int $price
33+
* @param int $count
34+
*/
35+
public function __construct(int $price, int $count)
36+
{
37+
$this->price = $price;
38+
$this->count = $count;
39+
}
40+
41+
/**
42+
* @return int
43+
*/
44+
public function getPrice(): int
45+
{
46+
return $this->price;
47+
}
48+
49+
/**
50+
* @return int
51+
*/
52+
public function getCount(): int
53+
{
54+
return $this->count;
55+
}
56+
}
57+
```
58+
59+
再來定義規格模組(Specification)的介面,這個介面會需要實作大頭菜尚未補足的邏輯,也就是計算鈴錢價格(calculatePrice)的這項功能。
60+
61+
Specification.php
62+
```php
63+
/**
64+
* Interface Specification.
65+
*/
66+
interface Specification
67+
{
68+
/**
69+
* @return int
70+
*/
71+
public function calculatePrice(): int;
72+
}
73+
```
74+
75+
最後我們有兩種規格模式,分別是正常的大頭菜、壞掉的大頭菜,我們先來實作正常的情況下,大頭菜的總計鈴錢計算規格,這裡提供了可以丟大頭菜集合的方式,無論你有多少顆大頭菜,你通通都丟過來,我會全部加總一起算,就算是一顆也沒問題。
76+
77+
TurnipsSpecification.php
78+
```php
79+
/**
80+
* Class TurnipsSpecification.
81+
*/
82+
class TurnipsSpecification implements Specification
83+
{
84+
/**
85+
* @var Turnips[]
86+
*/
87+
protected array $turnips;
88+
89+
/**
90+
* Turnips constructor.
91+
*
92+
* @param Turnips[] $turnips
93+
*/
94+
public function __construct(Turnips ...$turnips)
95+
{
96+
$this->turnips = $turnips;
97+
}
98+
99+
/**
100+
* @return int
101+
*/
102+
public function calculatePrice(): int
103+
{
104+
$total = 0;
105+
foreach ($this->turnips as $turnip) {
106+
$total += $turnip->getPrice() * $turnip->getCount();
107+
}
108+
109+
return $total;
110+
}
111+
}
112+
```
113+
114+
最後是實作壞掉的大頭菜計算模式,這裡也是一樣提供一顆或多顆大頭菜計算,不一樣的點在於因為是壞掉的大頭菜,無法賣出鈴錢,所以無論你丟過來幾顆,我都會回傳給你 0 鈴錢。
115+
116+
```php
117+
/**
118+
* Class SpoiledSpecification.
119+
*/
120+
class SpoiledSpecification implements Specification
121+
{
122+
/**
123+
* @var Specification[]
124+
*/
125+
protected array $turnips;
126+
127+
/**
128+
* SpoiledSpecification constructor.
129+
*
130+
* @param Specification[] $turnips
131+
*/
132+
public function __construct(Specification ...$turnips)
133+
{
134+
$this->turnips = $turnips;
135+
}
136+
137+
/**
138+
* @return int
139+
*/
140+
public function calculatePrice(): int
141+
{
142+
return 0;
143+
}
144+
}
145+
```
10146

11147
## 測試
148+
最後我們要寫個測試,來測試規格模式是不是正確的,主要分別為單顆大頭菜與多顆大頭菜的狀況下,以及正常、壞掉的兩種規格模式的測試。
149+
1. 單顆大頭菜使用正常規格模組,是否能正常計算出鈴錢價格。
150+
2. 多顆大頭菜使用正常規格模組,是否能正常計算出鈴錢價格。
151+
3. 單顆大頭菜使用正常規格模組,再套用壞掉的規格,是否能正常計算出壞掉的鈴錢價格。
152+
4. 多顆大頭菜使用正常規格模組,再套用壞掉的規格,是否能正常計算出壞掉的鈴錢價格。
153+
154+
SpecificationPatternTest.php
155+
```php
156+
/**
157+
* Class SpecificationPatternTest.
158+
*/
159+
class SpecificationPatternTest extends TestCase
160+
{
161+
/**
162+
* @test
163+
*/
164+
public function test_single_turnips()
165+
{
166+
$turnips = new Turnips(100, 40);
167+
$specification = new TurnipsSpecification($turnips);
168+
169+
$this->assertEquals(4000, $specification->calculatePrice());
170+
}
12171

172+
/**
173+
* @test
174+
*/
175+
public function test_multi_turnips()
176+
{
177+
$turnips_A = new Turnips(100, 40);
178+
$turnips_B = new Turnips(90, 20);
179+
$turnips_C = new Turnips(110, 20);
180+
$specification = new TurnipsSpecification($turnips_A, $turnips_B, $turnips_C);
181+
182+
$this->assertEquals(8000, $specification->calculatePrice());
183+
}
184+
185+
/**
186+
* @test
187+
*/
188+
public function test_single_spoiled()
189+
{
190+
$turnips = new Turnips(100, 40);
191+
$specification = new TurnipsSpecification($turnips);
192+
$spoiled = new SpoiledSpecification($specification);
193+
194+
$this->assertEquals(0, $spoiled->calculatePrice());
195+
}
196+
197+
/**
198+
* @test
199+
*/
200+
public function test_multi_spoiled()
201+
{
202+
$turnips_A = new Turnips(100, 40);
203+
$turnips_B = new Turnips(90, 20);
204+
$turnips_C = new Turnips(110, 20);
205+
$specification = new TurnipsSpecification($turnips_A, $turnips_B, $turnips_C);
206+
$spoiled = new SpoiledSpecification($specification);
207+
208+
$this->assertEquals(0, $spoiled->calculatePrice());
209+
}
210+
}
211+
```
13212

14213
最後測試的執行結果會獲得如下:
15214

@@ -20,6 +219,14 @@ PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors.
20219
PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
21220
22221
222+
==> ...fResponsibilitiesTest ✔ ✔ ✔
223+
==> CommandPatternTest ✔
224+
==> IteratorPatternTest ✔ ✔ ✔ ✔
225+
==> MediatorPatternTest ✔ ✔ ✔
226+
==> MementoPatternTest ✔
227+
==> NullObjectPatternTest ✔ ✔ ✔ ✔
228+
==> ObserverPatternTest ✔
229+
==> SpecificationPatternTest ✔ ✔ ✔ ✔
23230
==> AbstractFactoryTest ✔ ✔ ✔ ✔
24231
==> BuilderPatternTest ✔ ✔ ✔ ✔
25232
==> FactoryMethodTest ✔ ✔ ✔ ✔
@@ -40,9 +247,9 @@ PHPUnit 9.2.6 by Sebastian Bergmann and contributors.
40247
==> ProxyPatternTest ✔ ✔
41248
==> RegistryPatternTest ✔ ✔ ✔ ✔ ✔
42249
43-
Time: 00:00.037, Memory: 6.00 MB
250+
Time: 00:00.036, Memory: 8.00 MB
44251
45-
OK (51 tests, 116 assertions)
252+
OK (72 tests, 141 assertions)
46253
```
47254

48255
## 完整程式碼
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\SpecificationPattern;
4+
5+
/**
6+
* Interface Specification.
7+
*/
8+
interface Specification
9+
{
10+
/**
11+
* @return int
12+
*/
13+
public function calculatePrice(): int;
14+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\SpecificationPattern;
4+
5+
/**
6+
* Class SpoiledSpecification.
7+
*/
8+
class SpoiledSpecification implements Specification
9+
{
10+
/**
11+
* @var Specification[]
12+
*/
13+
protected array $turnips;
14+
15+
/**
16+
* SpoiledSpecification constructor.
17+
*
18+
* @param Specification[] $turnips
19+
*/
20+
public function __construct(Specification ...$turnips)
21+
{
22+
$this->turnips = $turnips;
23+
}
24+
25+
/**
26+
* @return int
27+
*/
28+
public function calculatePrice(): int
29+
{
30+
return 0;
31+
}
32+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\SpecificationPattern;
4+
5+
/**
6+
* Class Turnips.
7+
*/
8+
class Turnips
9+
{
10+
/**
11+
* @var int
12+
*/
13+
protected int $price = 0;
14+
15+
/**
16+
* @var int
17+
*/
18+
protected int $count = 0;
19+
20+
/**
21+
* Turnips constructor.
22+
*
23+
* @param int $price
24+
* @param int $count
25+
*/
26+
public function __construct(int $price, int $count)
27+
{
28+
$this->price = $price;
29+
$this->count = $count;
30+
}
31+
32+
/**
33+
* @return int
34+
*/
35+
public function getPrice(): int
36+
{
37+
return $this->price;
38+
}
39+
40+
/**
41+
* @return int
42+
*/
43+
public function getCount(): int
44+
{
45+
return $this->count;
46+
}
47+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace DesignPatterns\Behavioral\SpecificationPattern;
4+
5+
/**
6+
* Class TurnipsSpecification.
7+
*/
8+
class TurnipsSpecification implements Specification
9+
{
10+
/**
11+
* @var Turnips[]
12+
*/
13+
protected array $turnips;
14+
15+
/**
16+
* Turnips constructor.
17+
*
18+
* @param Turnips[] $turnips
19+
*/
20+
public function __construct(Turnips ...$turnips)
21+
{
22+
$this->turnips = $turnips;
23+
}
24+
25+
/**
26+
* @return int
27+
*/
28+
public function calculatePrice(): int
29+
{
30+
$total = 0;
31+
foreach ($this->turnips as $turnip) {
32+
$total += $turnip->getPrice() * $turnip->getCount();
33+
}
34+
35+
return $total;
36+
}
37+
}
92.5 KB
Loading

0 commit comments

Comments
 (0)