Design patterns: Object pool

Sławomir Kowalski
May 1, 2018 · 9 min read
Image for post
Image for post

In this article, I will be talking about the design pattern, which is called Object pool, I wanted to describe it now because it is quite similar to the Flyweight pattern.

Intent

  • Keeping initialized objects in the pool ready to use.
  • Returning the object on which the client performed operations back to the pool.
  • Not creating costly classes repeatedly to create, at once created expensive objects are returned back to the pool.

Problem

The object pool pattern as like Flyweight is related to caching, on the principle just that once created object by the client, it is returned back to the pool, when the client will need it again, the object will be retrieved from the pool, but it will not be created again from the beginning because it has already been created, you can also set the number of objects that can be stored in the pool of objects.

Use when:

  • You must create objects that are expensive to create
  • The frequency of creating further objects is also high.
  • The number of objects in use is small.

Discussion

It must be remembered that when we return an object to Object pool, its state must be reset, if we return the object on which the client performed some operations, then after another need to use it by the client, he can perform actions not as we would like. The object pool which doesn’t reset the states of returned objects is an anti-pattern. The object reset mechanism must be implemented in the Object pool, the client should not have to reset objects because it is not the client that is responsible for resetting the objects, only the pool.

Structure

The UML diagram clearly shows that Object pool class is the singleton, because there is a getInstance() method that creates an object in the class. The method acquireReusable(), that is responsible for creating the object and saving it to the pool as a used object. The method releaseReusable(), which is responsible for releasing the object from the client, and inserting it into the objects available for use in Pula, but already unused, in practical examples this will be better explained.

Image for post
Image for post

Example

Below is an example of the implementation of the Object pool design pattern

namespace ObjectPool
{
class ObjectPool<T> where T : new()
{
private static List<T> _available = new List<T>();
private List<T> _inUse = new List<T>();

private int counter = 0;
private int MAXTotalObjects;

private static ObjectPool<T> instance = null;

public static ObjectPool<T> GetInstance()
{
if (instance == null)
{
instance = new ObjectPool<T>();
}
else
{
Console.WriteLine("This is singleton!");
}
return instance;
}

public T acquireReusable()
{
if (_available.Count != 0 && _available.Count < 10)
{
T item = _available[0];
_inUse.Add(item);
_available.RemoveAt(0);
counter--;
return item;
}
else
{
T obj = new T();
_inUse.Add(obj);
return obj;
}
}

public void ReleaseReusable(T item)
{
if (counter <= MAXTotalObjects)
{
_available.Add(item);
counter++;
_inUse.Remove(item);
}
else
{
Console.WriteLine("To much object in pool!");
}
}

public void SetMaxPoolSize(int settingPoolSize)
{
MAXTotalObjects = settingPoolSize;
}
}

class PooledObject
{
DateTime _createdAt = DateTime.Now;

public DateTime CreatedAt
{
get { return _createdAt; }
}

}

class Program
{
static void Main(string[] args)
{
ObjectPool<PooledObject> objPool = ObjectPool<PooledObject>.GetInstance();
objPool.SetMaxPoolSize(10);
PooledObject obj = objPool.acquireReusable();
objPool.ReleaseReusable(obj);
Console.WriteLine(obj.CreatedAt);
Console.ReadKey();
}
}
}
Image for post
Image for post
Image for post
Image for post
namespace AsynchronousObjectPool
{
class ObjectPool<T> where T : new()
{
private static List<T> _available = new List<T>();
private List<T> _inUse = new List<T>();

private int counter = 0;
private int MAXTotalObjects;

private static ObjectPool<T> instance = null;

public static ObjectPool<T> GetInstance()
{
lock (_available)
{
if (instance == null)
{
instance = new ObjectPool<T>();
}
else
{
Console.WriteLine("This is singleton!");
}
return instance;
}
}

public T acquireReusable()
{
lock (_available)
{
if (_available.Count != 0 && _available.Count < 10)
{
T item = _available[0];
_inUse.Add(item);
_available.RemoveAt(0);
counter--;
return item;
}
else
{
T obj = new T();
_inUse.Add(obj);
return obj;
}
}
}

public void ReleaseReusable(T item)
{
lock (_available)
{
if (counter < MAXTotalObjects)
{
_available.Add(item);
counter++;
_inUse.Remove(item);
}
else
{
Console.WriteLine("To much object in pool!");
}
}
}

public void SetMaxPoolSize(int settingPoolSize)
{
MAXTotalObjects = settingPoolSize;
}
}

class Program
{
static void Main(string[] args)
{
Task task1 = Task.Run(() =>
{
ObjectPool<PooledObject> objPool = ObjectPool<PooledObject>.GetInstance();
objPool.SetMaxPoolSize(10);
PooledObject obj = objPool.acquireReusable();
objPool.ReleaseReusable(obj);
Console.WriteLine(obj.CreatedAt);
Console.WriteLine("This is the first thread");
});

Task task2 = Task.Run(() =>
{
ObjectPool<PooledObject> objPool = ObjectPool<PooledObject>.GetInstance();
objPool.SetMaxPoolSize(10);
PooledObject obj = objPool.acquireReusable();
objPool.ReleaseReusable(obj);
Console.WriteLine(obj.CreatedAt);
Console.WriteLine("This is the second thread");
});

Console.ReadKey();
}
}

class PooledObject
{
DateTime _createdAt = DateTime.Now;

public DateTime CreatedAt
{
get { return _createdAt; }
}

}
}
Image for post
Image for post
Image for post
Image for post

An example from of life taken

Warehouse

We will make an example on a warehouse basis, when the owner hires an employee, takes the appropriate equipment from the warehouse and gives it to the employee and when the employee is released, the equipment is returned to the warehouse, this is shown well in the picture below.

Image for post
Image for post
namespace Warehouse
{
class Warehouse<T> where T : new()
{
private static List<T> _availableEquipment = new List<T>();
private List<T> _inUseEquipment = new List<T>();

public int counter = 0;
private int MAXTotalObjects;

private static Warehouse<T> instance = null;

public static Warehouse<T> GetInstance()
{
if (instance == null)
{
instance = new Warehouse<T>();
}
else
{
Console.WriteLine("This is singleton!");
}
return instance;
}

public T GiveEquipmentWorker()
{
if (_availableEquipment.Count != 0 && _availableEquipment.Count < 10)
{
T item = _availableEquipment[0];
_inUseEquipment.Add(item);
_availableEquipment.RemoveAt(0);
counter--;
return item;
}
else
{
T obj = new T();
_inUseEquipment.Add(obj);
return obj;
}
}

public void ReturnEquipmentToWarehouse(T item)
{
if (counter <= MAXTotalObjects)
{
_availableEquipment.Add(item);
counter++;
_inUseEquipment.Remove(item);
}
else
{
Console.WriteLine("To much object in pool!");
}
}

public void SetMaxPoolSize(int settingPoolSize)
{
MAXTotalObjects = settingPoolSize;
}
}
}
namespace Warehouse
{
interface IWorkSpace
{
void IfWorkerWasEmployed();
void IfWorkerWasFired();
}

class WorkSpace : IWorkSpace
{
public void IfWorkerWasEmployed()
{
Console.WriteLine("Worker work in shop");
}

public void IfWorkerWasFired()
{
Console.WriteLine("The employee was released");
}
}

class Store
{
public int workers;
public Warehouse<WorkSpace> objPool;

public void employWorker()
{
workers++;
}

public void OrderEquipment()
{
objPool = Warehouse<WorkSpace>.GetInstance();
objPool.SetMaxPoolSize(1);
}

public int employees()
{
return workers;
}

public void FireAnEmployee(WorkSpace obj)
{
workers--;
objPool.ReturnEquipmentToWarehouse(obj);
}

public void CheckThatWorkerWasFired(WorkSpace obj, bool ifEmployeeWorker)
{
if (ifEmployeeWorker == true)
{
obj.IfWorkerWasEmployed();
}
else if (ifEmployeeWorker == false)
{
obj.IfWorkerWasFired();
}
}
}
}
class Program
{
static void Main(string[] args)
{
Store store = new Store();
store.employWorker();
store.OrderEquipment();
WorkSpace obj = store.objPool.GiveEquipmentWorker();

Console.WriteLine("The value of the counter in the Warehouse class: " + store.objPool.counter);

store.CheckThatWorkerWasFired(obj,true);

Console.WriteLine(store.workers);

store.FireAnEmployee(obj);

Console.WriteLine("The value of the counter in the Warehouse class: "+store.objPool.counter);

store.CheckThatWorkerWasFired(obj, false);

Console.WriteLine(store.workers);

Console.ReadKey();
}
}
Image for post
Image for post

Flyweight and Object Pool differences

  • The main difference is that Flyweight resources are immutable and the Object pool are mutable.
  • In the Object pool, at any given moment the object can be accessed only for one client, in Flyweight many clients can simultaneously use the same object.

Summary

And that’s all about the Object pool and, in general, about constructional and structural patterns in the next article on the Interpreter design pattern. As I have recently many activities, so, an Interpreter design pattern will be an entry for two weeks, unfortunately life :)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store