Object Pool
- Object pooling can offer a significant performance boost; it is most effective in situations where:
- The cost of initializing a class instance is high
- The rate of instantiation of a class is high
- The number of instantiations in use at any one time is low.
- Object pools are used to manage the object caching.
- A client with access to a Object pool can avoid creating a new Objects by simply asking the pool for one that has already been instantiated instead.
- It is desirable to keep all Reusable objects that are not currently in use in the same object pool so that they can be managed by one coherent policy.
- To achieve this, the Reusable Pool class is designed to be a singleton class.
- In many applications of the Object Pool pattern, there are reasons for limiting the total number of
Reusable
objects that may exist.
- In such cases, the ReusablePool object that creates Reusable objects is responsible for not creating more than a specified maximum number of
Reusable
objects.
Participants
Reusable
(ExpensiveClass)
- It collaborates with other objects for a limited amount of time, then they are no longer needed.
- Usually, it is desirable to keep all Reusable objects that are not currently in use in the same object pool so that they can be managed by one coherent policy.
ReusablePool
(ObjectPool<T>)
- It maintains the collection of Reusable objects that are not currently in use.
var objPool = new ObjectPool<ExpensiveClass>();
var obj = objPool.Get();
objPool.Release(obj);
public class ExpensiveClass
{
public int MyProperty { get; set; }
}
public class ObjectPool<T> where T : new()
{
private readonly ConcurrentBag<T> _Items = new ConcurrentBag<T>();
private int _Counter = 0;
private readonly int Max = 10;
public T Get()
{
if (_Items.TryTake(out T item))
{
_Counter--;
return item;
}
else
{
var obj = new T();
_Items.Add(obj);
_Counter++;
return obj;
}
}
public void Release(T item)
{
if (_Counter < Max)
{
_Items.Add(item);
_Counter++;
}
}
}
How to: Create an Object Pool by Using a ConcurrentBag
Rules of Thumb
- The Factory Method pattern can be used to encapsulate the creation logic for objects.
- However, it does not manage them after their creation, the object pool pattern keeps track of the objects it creates.
- Object Pools are usually implemented as Singletons.
Service Locator
- It is used to encapsulate the processes involved in obtaining a service with a strong abstraction layer.
- This pattern uses a central registry known as the "service locator", which on request returns the information necessary to perform a certain task.
- You have classes with dependencies on services whose concrete types are specified at compile time.
- Use the Service Locator pattern to achieve any of the following objectives:
- You want to decouple your classes from their dependencies so that these dependencies can be replaced or updated with little or no change to the classes.
- You want to write logic that depends on classes whose concrete implementation is not known at compile time.
- You want to be able to test your classes in isolation, without the dependencies.
- You do not want the logic that locates and manages the dependencies to be in your classes.
- You want to divide your application into loosely coupled modules that can be independently developed, tested, versioned, and deployed.
- Create a service locator that contains references to the services and that encapsulates the logic that locates them.
- In your classes, use the service locator to obtain service instances. The following diagram illustrates how classes use a service locator.
- A service locator should be able to locate a service without knowing its concrete type.
- For example, it might use a string key or a service interface type.
- This allows you to replace the concrete implementation of the dependency without modifying the classes.
public static class Locator
{
private readonly static Dictionary<Type, Func<object>> _Services = new Dictionary<Type, Func<object>>();
public static void Register<T>(Func<T> resolver) => _Services[typeof(T)] = () => resolver();
public static T Resolve<T>() => (T)_Services[typeof(T)]();
public static void Reset() => _Services.Clear();
}