物件池模式 Pool Pattern

物件池模式,每次的買賣都是致富的關鍵,致富不能只靠 40 顆大頭菜,靠的是放滿整座島的大頭菜,因此你需要有個島專門放大頭菜,放得滿滿的,到了關鍵時刻再把大頭菜拿出來賣。

UML

UML

實作

首先我們會需要把大頭菜定義出來,並且賦予幾些簡單的功能。

Turnips.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* Class Turnips.
*/
class Turnips
{
/**
* @var int
*/
protected $price;

/**
* @var int
*/
protected $count;

/**
* Turnips constructor.
*
* @param int $price
* @param int $count
*/
public function __construct(int $price, int $count)
{
$this->price = $price;
$this->count = $count;
}

/**
* @return int
*/
public function calculatePrice(): int
{
if (isset($this->price) && isset($this->count)) {
return $this->price * $this->count;
} else {
return 0;
}
}
}

再來我們要做一個大頭菜池,而且要挖深,挖深呢,好處是有利於儲大頭菜,未來還可以養大頭菜,然後就可以把大頭菜通通丟進去。

TurnipsPool.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use Countable;

/**
* Class TurnipsPool.
*/
class TurnipsPool implements Countable
{
/**
* @var Turnips[]
*/
protected $pool = [];

/**
* @var int
*/
protected $total = 0;

/**
* @return Turnips
*/
public function get(string $key = null): Turnips
{
if (isset($key)) {
$turnips = $this->pool[$key];
unset($this->pool[$key]);
} else {
$turnips = array_pop($this->pool);
}

$this->total -= $turnips->calculatePrice();

return $turnips;
}

/**
* 把大頭菜塞到池子裡
*
* @param Turnips $turnips
*
* @return string
*/
public function set(Turnips $turnips): string
{
$key = spl_object_hash($turnips);
$this->total += $turnips->calculatePrice();
$this->pool[$key] = $turnips;

return $key;
}

/**
* @return int
*/
public function total(): int
{
return $this->total;
}

/**
* @return int
*/
public function count(): int
{
return count($this->pool);
}
}

額外補充

Countable

繼承 Countable 這個類別可以使用 count() 這個方法,因此需要實作它。

1
2
3
4
class Countable {
/* Methods */
abstract public count ( void ) : int
}

測試

最後為了測試我們挖很深的大頭菜池是否能夠養大頭菜,所以我們有兩組測試要做:

  1. 測試是否能夠正常的新增 10 組大頭菜,並且把大頭菜拿出 2 組後,檢查池子裡面是否剩下 8 組大頭菜,然後比較一下拿出來的這 2 組是不是兩個不同的大頭菜,最後比較一下大頭菜池子裡的大頭菜價格是不是正確的。
  2. 測試是否能夠正常的新增 10 組大頭菜,並且把大頭菜拿出 1 組後,馬上把大頭菜丟回去池子裡,再從池子裡拿出 1 組大頭菜,檢查池子裡面是否剩下 9 組大頭菜,然後比較一下最後拿出來的這組,是不是就是一開始拿出來的那組大頭菜,最後比較一下大頭菜池子裡的大頭菜價格是不是正確的。

PoolPatternTest.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* Class PoolPatternTest.
*/
class PoolPatternTest extends TestCase
{
/**
* 測試是否能夠正常的新增 10 組大頭菜,
* 並且把大頭菜拿出 2 組後,檢查池子裡面是否剩下 8 組大頭菜,
* 然後比較一下拿出來的這 2 組是不是兩個不同的大頭菜,
* 最後比較一下大頭菜池子裡的大頭菜價格是不是正確的。
*
* @test
*/
public function test_can_set_new_turnips_and_get()
{
$pool = new TurnipsPool();
for ($i = 0; $i < 10; $i++) {
$turnips = new Turnips(100, 40);
$pool->set($turnips);
}

$turnips1 = $pool->get();
$turnips2 = $pool->get();

$this->assertCount(8, $pool);
$this->assertNotSame($turnips1, $turnips2);
$this->assertEquals(32000, $pool->total());
}

/**
* 測試是否能夠正常的新增 10 組大頭菜,
* 並且把大頭菜拿出 1 組後,馬上把大頭菜丟回去池子裡,
* 再從池子裡拿出 1 組大頭菜,
* 檢查池子裡面是否剩下 9 組大頭菜,
* 然後比較一下最後拿出來的這組,是不是就是一開始拿出來的那組大頭菜,
* 最後比較一下大頭菜池子裡的大頭菜價格是不是正確的。
*
* @test
*/
public function test_can_get_turnips_twice_when_set_it_first()
{
$pool = new TurnipsPool();
for ($i = 0; $i < 10; $i++) {
$turnips = new Turnips(100, 40);
$pool->set($turnips);
}

$turnips1 = $pool->get();
$pool->set($turnips1);

$turnips2 = $pool->get();

$this->assertCount(9, $pool);
$this->assertSame($turnips1, $turnips2);
$this->assertEquals(36000, $pool->total());
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors.
==> Configuration: ~/php-design-pattern/vendor/codedungeon/phpunit-result-printer/src/phpunit-printer.yml

PHPUnit 9.2.6 by Sebastian Bergmann and contributors.


==> AbstractFactoryTest ✔ ✔ ✔ ✔
==> BuilderPatternTest ✔ ✔
==> FactoryMethodTest ✔ ✔ ✔ ✔
==> PoolPatternTest ✔ ✔
==> PrototypePatternTest ✔ ✔
==> SimpleFactoryTest ✔ ✔ ✔ ✔
==> SingletonPatternTest ✔
==> StaticFactoryTest ✔ ✔ ✔ ✔ ✔

Time: 00:00.050, Memory: 6.00 MB

OK (24 tests, 68 assertions)

完整程式碼

設計模式不難,找回快樂而已,以大頭菜為例。

參考文獻