1.5. Pula obiektów (Pool)

1.5.1. Purpose

Wzorzec Puli obiektów wzorcem, który polega na użyciu puli obiektów. Pula obiektów to zbiór zainicjowanych obiektów, które są trzymane w gotowości do użycia (zamiast je alokować lub dealokować na żądanie). Klient puli obiektów żąda obiektu z tej puli i wykonuje na tym obiekcie jakieś operacje. Po skończeniu, zamiast niszczyć obiekt - zwraca go do puli. Jest to szczególny typ obiektu fabrykującego.

Użycie puli obiektów może przyczynić się do znacznego wzrostu wydajności wtedy, gdy: koszt inicjalizacji instancji klasy jest wysoki, częstotliwość tworzenia kolejnych obiektów klasy jest wysoka, liczba instancji klas będących w użyciu jest mała. Obiekt z puli jest dostarczany w przewidywalnym czasie, podczas gdy czas tworzenia nowego obiektu (szczególnie przez sieć) może być duży.

Korzyści te są w większości odczuwalne dla takich obiektów jak: połączenia bazodanowe, połączenia gniazdowe, wątki, duże obiekty graficzne (takie jak fonty i bitmapy). Użycie puli dla prostych obiektów (które nie wskazują na zewnętrzne zasoby, lecz tylko zajmują pamięć), nie jest tak samo efektywne i może nawet obniżyć wydajność.

1.5.2. Diagram UML

Alt Pool UML Diagram

1.5.3. Kod

Ten kod znajdziesz również na 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
<?php

namespace DesignPatterns\Creational\Pool;

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

    /**
     * @var StringReverseWorker[]
     */
    private $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
21
<?php

namespace DesignPatterns\Creational\Pool;

class StringReverseWorker
{
    /**
     * @var \DateTime
     */
    private $createdAt;

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

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

1.5.4. Testy

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

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);
    }
}