1.4. Object Pool パターン

1.4.1. 目的

Object Pool パターン は、オブジェクトを必要な場合に割当て利用後に破棄するのではなく、初期化済みのいつでも利用可能なオブジェクトを"プール"に保持し、それらを使用する生成に関するデザインパターンです。このプールのClientクラスは、プールにオブジェクトを要求し、プールから返却されたオブジェクトを使用します。クライアントはオブジェクトの使用終了後、オブジェクト(特定の型のFacboryオブジェクト)を破棄するでのはなくプールに返却します。

オブジェクトプーリングは、クラスインスタンスの初期化のコストが高く、クラスをインスタンス化する割合が高く、一度に使用されるインスタンスの数が少ない状況で、パフォーマンスを大幅に向上させることができます。また、新しいオブジェクトの生成に時間がかかる可能性がある場合(特にネットワーク経由での生成の場合)に、プールされたオブジェクトを取得することで予測可能な時間でオブジェクトを得ることができるようになります。

しかし、これらのメリットは、データベース接続、ソケット接続、スレッド、フォントやビットマップのような大きなグラフィックオブジェクトなど、時間に関してコストがかかるオブジェクトのほとんどに当てはまります。特定の状況下では、単純なオブジェクトプーリング(外部リソースを保持せず、メモリを占有するだけ)は効率的でなく、パフォーマンスが低下する可能性があります。

1.4.2. クラス図

Alt Pool UML Diagram

1.4.3. サンプルコード

サンプルコードは GitHub でも確認することができます。

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
47
48
<?php

declare(strict_types=1);

namespace DesignPatterns\Creational\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
<?php

declare(strict_types=1);

namespace DesignPatterns\Creational\Pool;

use DateTime;

class StringReverseWorker
{
    public function __construct()
    {
    }

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

1.4.4. テスト

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
31
32
<?php

declare(strict_types=1);

namespace DesignPatterns\Creational\Pool\Tests;

use DesignPatterns\Creational\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);
    }
}