对象池模式

目的

对象池模式是一种创建型设计模式(译者注:该模式不是 23 种经典模式之一,因此,把它放在划分到扩展模式分类下),它使用一组随时准备使用的初始化对象 — 池,而不是按需分配和销毁它们。客户端将从对象池中请求一个对象,并对返回的对象执行操作。当客户端完成后,它将对象(这是一种特定类型的工厂对象)返回到池中,而不是销毁它。

以下几种场景,使用对象池可以显著地提升性能:

  • 初始化一个类实例的成本很高
  • 类的实例化频率很高
  • 使用的实例数量很低(任何时候)

当创建新对象(特别是通过网络)可能需要不定的时间时,可以在可预测的时间内获得池化对象。

然而,这些好处主要是对那些时间开销昂贵的对象而言,如:数据库连接、套接字连接、线程和字体或位图等大型图形对象。在某些情况下,简单的对象池(不持有外部资源,只占用内存)可能并不高效,而且可能会降低性能。

UML 类图

原型模式类图

代码

WorkerPool.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
<?php declare(strict_types = 1);

namespace DesignPatterns\Extend\Pool;

use Countable;

class WorkerPool implements Countable
{
/**
* @var StringReverseWorker[]
*/
private array $occupiedWorkers = [];

/**
* @var StringReverseWorker[]
*/
private array $freeWorkers = [];

public function get(): StringReverseWorker
{
if (count($this->freeWorkers) == 0) {
$worker = new StringReverseWorker();
} else {
$worker = array_pop($this->freeWorkers);
}

$this->occupiedWorkers[spl_object_hash($worker)] = $worker;

return $worker;
}

public function dispose(StringReverseWorker $worker)
{
$key = spl_object_hash($worker);

if (isset($this->occupiedWorkers[$key])) {
unset($this->occupiedWorkers[$key]);
$this->freeWorkers[$key] = $worker;
}
}

public function count(): int
{
return count($this->occupiedWorkers) + count($this->freeWorkers);
}
}

StringReverseWorker.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php declare(strict_types = 1);

namespace DesignPatterns\Extend\Pool;

use DateTime;

class StringReverseWorker
{
private DateTime $createdAt;

public function __construct()
{
$this->createdAt = new DateTime();
}

public function run(string $text)
{
return strrev($text);
}
}

测试

Tests/PoolTest.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
<?php declare(strict_types = 1);

namespace DesignPatterns\Extend\Pool\Tests;

use DesignPatterns\Extend\Pool\WorkerPool;
use PHPUnit\Framework\TestCase;

class PoolTest extends TestCase
{
public function testCanGetNewInstancesWithGet()
{
$pool = new WorkerPool();
$worker1 = $pool->get();
$worker2 = $pool->get();

$this->assertCount(2, $pool);
$this->assertNotSame($worker1, $worker2);
}

public function testCanGetSameInstanceTwiceWhenDisposingItFirst()
{
$pool = new WorkerPool();
$worker1 = $pool->get();
$pool->dispose($worker1);
$worker2 = $pool->get();

$this->assertCount(1, $pool);
$this->assertSame($worker1, $worker2);
}
}