1.4. Pool

1.4.1. Purpose

Het ** objectpoolpatroon ** is een ontwerppatroon van software dat gebruikmaakt van een set geïnitialiseerde objecten die klaar voor gebruik worden gehouden - een “pool” - in plaats van ze op aanvraag toe te wijzen en te vernietigen. Een client van de pool zal een object uit de pool opvragen en bewerkingen uitvoeren op het geretourneerde object. Wanneer de klant klaar is, brengt hij het object, dat een specifiek type fabrieksobject is, terug naar de pool in plaats van het te vernietigen.

Objectpooling kan een aanzienlijke prestatieverbetering bieden in situaties waarin de kosten voor het initialiseren van een klasse-instantie hoog zijn, de snelheid waarmee een klasse wordt geïnstantieerd en het aantal instanties dat tegelijkertijd in gebruik is, laag is. Het gepoolde object wordt binnen een voorspelbare tijd verkregen wanneer het maken van de nieuwe objecten (vooral via het netwerk) variabele tijd kan duren.

Deze voordelen gelden echter meestal voor objecten die duur zijn in de tijd, zoals databaseverbindingen, socketverbindingen, threads en grote grafische objecten zoals lettertypen of bitmaps. In bepaalde situaties is eenvoudige pooling van objecten (die geen externe bronnen bevatten, maar alleen geheugen innemen) mogelijk niet efficiënt en kunnen de prestaties afnemen.

1.4.2. UML Diagram

Alt Pool UML Diagram

1.4.3. Code

Je kan deze broncode terugvinden op 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}