Registry Design Pattern
The Registry Design Pattern is a creational pattern that allows you to maintain a well-defined place for storing and retrieving objects globally within an application. It is often used as a centralized database of objects, allowing components of the application to access shared instances without passing them around explicitly.
Key Concepts:
- Global Access: Provides a centralized way to access shared resources or objects across the system.
- Singleton-like Behavior: Objects stored in the registry are usually single instances, ensuring that there is only one instance of a particular object across the system.
- Lookup Mechanism: Objects are stored with keys (usually strings or classes), and they can be retrieved by referencing these keys.
When to Use:
- When you have a set of shared objects that need to be accessed by different parts of the application.
- To avoid dependency injection of shared objects into every class that needs them.
- In systems that have service locators or need a global object cache.
Structure:
- Registry: The central object that stores instances of objects.
- Key: Used to uniquely identify each object in the registry (commonly a string or class type).
- Value: The object or service being stored and retrieved.
- Lookup Mechanism: Method to register and retrieve objects.
Pros:
- Centralized Access: A single point to manage and retrieve objects across the system.
- Decoupling: Removes the need to pass shared objects explicitly around the system.
Cons:
- Global State: Since the registry holds global state, improper usage can lead to issues like hidden dependencies and hard-to-trace bugs.
- Singleton Pattern Overlap: Often used in combination with the Singleton pattern, it can lead to similar issues such as tight coupling and difficulties in testing.
Use Cases:
- Global resource management (e.g., database connections, configurations, service instances).
- Managing services in a dependency injection framework or service locator.
Use Case: Plugin Management System
Problem: You are building a web application that allows users to extend its functionality using plugins. Each plugin offers specific functionality, like data export, email notifications, or theme customization. As new plugins can be added dynamically, the system must manage these plugins efficiently, allowing them to be discovered and used without hardcoding references to each one.
Solution: A Registry Design Pattern can be used to manage the lifecycle of plugins. The registry acts as a central store where each plugin can register itself, and the application can look up and invoke these plugins when needed. This allows for a flexible architecture where new plugins can be easily added without modifying the core application.
Steps:
- Registry Class: Create a
PluginRegistry
class that acts as a central storage for all plugins. - Plugin Registration: Each plugin registers itself with the
PluginRegistry
when loaded. - Plugin Lookup: When the application needs to execute a plugin, it can look it up by its unique identifier or name and execute the associated functionality.
Plugin interface
public interface Plugin
{
void execute();
}
Concrete Plugins
public class DatabaseConnectionPlugin implements Plugin
{
public void execute()
{
System.out.println("Database Connection ...");
}
}
public class DataExportPlugin implements Plugin
{
public void execute()
{
System.out.println("Executing data export...");
}
}
public class EmailNotificationPlugin implements Plugin
{
public void execute()
{
System.out.println("Executing email notifications...");
}
}
Registry Class
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
// Registry class
public class PluginRegistry
{
private static volatile PluginRegistry instance;;
private Map<String, Plugin> registry = new ConcurrentHashMap<>();
// Private constructor for Singleton
private PluginRegistry() { }
// Get the Singleton instance
public static PluginRegistry getInstance()
{
if (instance == null)
{
synchronized (PluginRegistry.class)
{
if (instance == null)
{
instance = new PluginRegistry();
}
}
}
return instance;
}
// Register a plugin with a key
public void registerPlugin(String key, Plugin plugin)
{
registry.put(key, plugin);
}
// Get a plugin by key
public Plugin getPlugin(String key)
{
return registry.get(key);
}
}
public class Client
{
public static void main(String[] args)
{
PluginRegistry registry = PluginRegistry.getInstance();
registry.registerPlugin("emailNotification", new EmailNotificationPlugin());
registry.registerPlugin("dataExport", new DataExportPlugin());
registry.registerPlugin("dbConnection", new DatabaseConnectionPlugin());
Plugin emailPlugin = registry.getPlugin("emailNotification");
emailPlugin.execute();
Plugin dataExportPlugin = registry.getPlugin("dataExport");
dataExportPlugin.execute();
Plugin dbConnectionPlugin = registry.getPlugin("dbConnection");
dbConnectionPlugin.execute();
}
}
Executing email notifications...
Executing data export...
Database Connection ...