1.4. Pool

1.4.1. Purpose

Das Objekt Pool Pattern ist ein Erzeugungsmuster, das ein Set von initialisierten Objekten bereithält - ein „Pool“ - statt jedesmal ein neues Objekt zu erzeugen und wieder zu zerstören bei Bedarf. Der Client des Pools fragt ein Objekt an und erhält es von dem Pool, führt alle gewünschten Operationen aus und gibt anschließend das Objekt zurück. Das Objekt selbst ist wie eine spezielle Factory implementiert.

Dieses Muster kann die Performance einer Anwendung entscheidend verbessern, wenn die Erzeugung von Objekten teuer ist, oft neue Objekte erzeugt werden müssen und die gleichzeitig verwendete Anzahl von Instanzen eher gering ist. Das Objekt im Pool wird in konstanter Zeit erzeugt, wohingegen die Erzeugung neuer Objekte (vorallem über das Netzwerk) variable Zeit in Anspruch nimmt und bei hoher Auslastung zum Problem führen würde.

Diese Vorteile können vorallem bei teuren Objekterzeugungen ausgespielt werden, wie z.B. Datenbankverbindungen, Socketverbindungen, Threads und großen Grafikobjekten wie Schriften oder Bitmaps. In manchen Situationen, in denen Objekte nur Speicher verbrauchen, aber nicht von externen Resourcen abhängig sind, wird das Muster nicht effizient sein und kann stattdessen die Performance beinträchtigen.

1.4.2. UML Diagramm

Alt Pool UML Diagram

1.4.3. Code

Du findest den Code auch auf 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. Теst

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