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 Diyagramı

1.4.3. Kod

Bu kodu Github üzerinde de bulabilirsiniz.

WorkerPool.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Pool;
 6
 7use Countable;
 8
 9class WorkerPool implements Countable
10{
11    /**
12     * @var StringReverseWorker[]
13     */
14    private array $occupiedWorkers = [];
15
16    /**
17     * @var StringReverseWorker[]
18     */
19    private array $freeWorkers = [];
20
21    public function get(): StringReverseWorker
22    {
23        if (count($this->freeWorkers) === 0) {
24            $worker = new StringReverseWorker();
25        } else {
26            $worker = array_pop($this->freeWorkers);
27        }
28
29        $this->occupiedWorkers[spl_object_hash($worker)] = $worker;
30
31        return $worker;
32    }
33
34    public function dispose(StringReverseWorker $worker): void
35    {
36        $key = spl_object_hash($worker);
37        if (isset($this->occupiedWorkers[$key])) {
38            unset($this->occupiedWorkers[$key]);
39            $this->freeWorkers[$key] = $worker;
40        }
41    }
42
43    public function count(): int
44    {
45        return count($this->occupiedWorkers) + count($this->freeWorkers);
46    }
47}

StringReverseWorker.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Pool;
 6
 7class StringReverseWorker
 8{
 9    public function run(string $text): string
10    {
11        return strrev($text);
12    }
13}

1.4.4. Test

Tests/PoolTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Pool\Tests;
 6
 7use DesignPatterns\Creational\Pool\WorkerPool;
 8use PHPUnit\Framework\TestCase;
 9
10class PoolTest extends TestCase
11{
12    public function testCanGetNewInstancesWithGet()
13    {
14        $pool = new WorkerPool();
15        $worker1 = $pool->get();
16        $worker2 = $pool->get();
17
18        $this->assertCount(2, $pool);
19        $this->assertNotSame($worker1, $worker2);
20    }
21
22    public function testCanGetSameInstanceTwiceWhenDisposingItFirst()
23    {
24        $pool = new WorkerPool();
25        $worker1 = $pool->get();
26        $pool->dispose($worker1);
27        $worker2 = $pool->get();
28
29        $this->assertCount(1, $pool);
30        $this->assertSame($worker1, $worker2);
31    }
32}