1.4. Pool

1.4.1. Amaç

İstek üzerine tahsis ve tahrip etmek yerine (construct/destruct) yerine, bir dizi örneklenmiş (initialized) nesneyi bir “havuz” içerisinde kullanıma hazır halde tutmak. İstemci havuzdan bir nesne ister ve döndürülen nesne üzerinden işlem yapar. Ancak (dönen nesneyle) işi bittiğinde ise, belirli bir fabrika nesnesi türü olan o nesneyi, yok etmek yerine havuza geri döndürür.

Nesne havuzlama, bir sınıfın başlatılma veya örneklenme (initialization/instantiation) maliyetlerinin yüksek olduğu ve herhangi bir anda kullanılan örneklerin sayısının düşük olduğu durumlarda belirgin bir verim artışı (performance boost) sağlayabilir. Yeni nesnelerin oluşturulması (özellikle ağ üzerinden) değişken zamanlar alabiliyorken, havuzlanan nesne öngörülebilir bir zamanda elde edilir.

Bununla birlikte bu getiriler genellikle maliyetli nesneler için geçerlidir (örn: veritabanı veya soket bağlantıları, iş parçacıkları (thread), font veya bitmap gibi büyük grafik nesneleri). Bazı durumlarda basit nesne havuzu (harici kaynakları tutmaz, yalnızca bellek kaplar) etkin olmadığı gibi verimi de düşürebilir.

1.4.2. UML Diyagramı

Alt Pool UML Diagram

1.4.3. Kod

Bu kodu Github üzerinde de bulabilirsiniz.

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. Test

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