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<?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. Теst

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}