One day, I promise I'll share something thrilling—like that time at band camp, the unfortunate incident where my dad decided my hand was optional, or when I saved the world by flying my fighter jet straight into an alien mothership’s death beam, sacrificing myself to take it down.
But today isn’t that day. Today, we’re jumping head first into the Object Pool pattern.
So what is the Object Pool design pattern?
It manages a set of reusable objects, reducing the overhead of creating and destroying objects repeatedly. It's especially useful in scenarios where object initialisation is expensive, such as database connections, threads, or network sockets.
An quick example
Here’s a PHP example demonstrating the Object Pool design pattern for managing reusable objects, such as database connections:
Reusable objects
First, we need a set of reusable objects, here’s some quick examples, a DatabaseConnection and a FileHandler class.
class DatabaseConnection {
public function connect(): void {
// Connect to DB
}
public function disconnect(): void {
// Disconnect from DB
}
}
class FileHandler {
public function open(string $filename, string $mode) {
// Open File
}
public function close(): void {
// Close File
}
}Object pool class
This manages the pool of objects.
class ObjectPool
{
private array $available = [];
private array $inUse = [];
public function __construct(private string $objectType)
{
if (!class_exists($objectType)) {
throw new InvalidArgumentException("Class $objectType does not exist.");
}
}
public function acquire(): object
{
if (empty($this->available)) {
$object = new $this->objectType();
} else {
$object = array_pop($this->available);
}
$this->inUse[spl_object_id($object)] = $object;
return $object;
}
public function release(object $object): void
{
$objectId = spl_object_id($object);
if (isset($this->inUse[$objectId])) {
unset($this->inUse[$objectId]);
$this->available[$objectId] = $object;
}
}
public function getCountAvailable(): int
{
return count($this->available);
}
public function getCountInUse(): int
{
return count($this->inUse);
}
}Let’s down this class down.
available: Stores unused instances.inUse: Tracks currently used instances.acquire(): Provides an object from the pool or creates a new one if none are available.release(): Returns an object to the pool for reuse.
Still with me? Let’s see it in action.
// Database Connect Example
$dbPool = new ObjectPool(DatabaseConnection::class);
$db1 = $dbPool->acquire(); // Creates new object.
$db1->connect();
$dbPool->release($db1);
$db2 = $dbPool->acquire(); // Uses existing object.
$db2->connect();
$dbPool->release($db2);
// File Handler Example
$filePool = new ObjectPool(FileHandler::class);
$file1 = $filePool->acquire();
$file1->open('example.txt');
$filePool->release($file1);Step-by-Step Breakdown
Database Connection Example
$dbPool = new ObjectPool(DatabaseConnection::class)An ObjectPool instance is created to manage DatabaseConnection objects. The ObjectPool is configured to handle objects of type DatabaseConnection.
$db1 = $dbPool->acquire(); // Creates new object.Since the pool is initially empty, calling acquire creates a new DatabaseConnection object.
This object is marked as "in use" and added to the inUse collection within the pool.
$db1->connect();
$dbPool->release($db1);The connect method is called on the object to simulate using the connection.
Releasing the object (release) returns it to the available collection, making it ready for reuse.
$db2 = $dbPool->acquire(); // Uses existing object.When acquire is called again, it checks the pool. An available DatabaseConnection object is found and reused.
This avoids creating a new instance, saving time and resources.
$db2->connect();
$dbPool->release($db2);The reused object is utilised again and then returned to the pool for future use.
File Handler Example
$filePool = new ObjectPool(FileHandler::class);A separate ObjectPool is created to manage FileHandler objects.
$file1 = $filePool->acquire(); // Creates new FileHandler.
$file1->open('example.txt');
$filePool->release($file1);A new FileHandler object is created, used to open a file (example.txt), and then returned to the pool for future reuse.
Key Concepts
Pool Initialisation: A collection (pool) of objects is initialised and made available for reuse.
Object Checkout: When a client needs an object, it requests one from the pool.
Object Return: Once the client is done, the object is returned to the pool for reuse.
Capacity Management: The pool can grow or shrink based on demand.
Advantages
Reduces the cost of object creation.
Improves performance for systems with frequent object instantiation.
Useful in resource-limited environments.
Disadvantages
Adds complexity to the system.
Potential for stale or improperly cleaned-up objects if not managed carefully.
Conclusion
Using an object pool in scenarios where object creation is expensive or limited, such as database connections or file handlers, ensures optimal resource usage, enhances performance, and simplifies resource management. It’s a robust solution for any application requiring efficient, reusable resources.
That said, for many modern applications, object pools might feel like overkill—especially when frameworks and libraries often handle resource management for you (e.g., connection pooling in database drivers).
However, understanding the object pool pattern gives you the ability to solve edge cases where fine-grained control is necessary and equips you with another tool for your development toolkit.
Even if you don’t use it, it’s a solid concept worth knowing!
Check out examples (with tests) within the code repository.
The Grumpy Dev is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.
