Singleton Pattern
The Singleton Pattern ensures that certain AI system components have only one instance and provides global access to that instance.
Pattern Overview
This pattern restricts instantiation of a class to one object, useful for coordinating actions across an AI system or managing shared resources.
Structure
use std::sync::{Arc, Mutex, Once};
// Configuration manager singleton
pub struct ConfigManager {
settings: std::collections::HashMap<String, String>,
}
impl ConfigManager {
fn new() -> Self {
let mut settings = std::collections::HashMap::new();
settings.insert("agent_timeout".to_string(), "30".to_string());
settings.insert("max_retries".to_string(), "3".to_string());
Self { settings }
}
pub fn get_setting(&self, key: &str) -> Option<&String> {
self.settings.get(key)
}
pub fn set_setting(&mut self, key: String, value: String) {
self.settings.insert(key, value);
}
}
// Thread-safe singleton implementation
static INIT: Once = Once::new();
static mut CONFIG_MANAGER: Option<Arc<Mutex<ConfigManager>>> = None;
impl ConfigManager {
pub fn instance() -> Arc<Mutex<ConfigManager>> {
unsafe {
INIT.call_once(|| {
CONFIG_MANAGER = Some(Arc::new(Mutex::new(ConfigManager::new())));
});
CONFIG_MANAGER.as_ref().unwrap().clone()
}
}
}
// Agent registry singleton
pub struct AgentRegistry {
agents: std::collections::HashMap<String, String>,
}
impl AgentRegistry {
fn new() -> Self {
Self {
agents: std::collections::HashMap::new(),
}
}
pub fn register_agent(&mut self, id: String, agent_type: String) {
self.agents.insert(id, agent_type);
}
pub fn get_agent_type(&self, id: &str) -> Option<&String> {
self.agents.get(id)
}
pub fn list_agents(&self) -> Vec<&String> {
self.agents.keys().collect()
}
}
static REGISTRY_INIT: Once = Once::new();
static mut AGENT_REGISTRY: Option<Arc<Mutex<AgentRegistry>>> = None;
impl AgentRegistry {
pub fn instance() -> Arc<Mutex<AgentRegistry>> {
unsafe {
REGISTRY_INIT.call_once(|| {
AGENT_REGISTRY = Some(Arc::new(Mutex::new(AgentRegistry::new())));
});
AGENT_REGISTRY.as_ref().unwrap().clone()
}
}
}Usage Example
// Access configuration manager
let config = ConfigManager::instance();
let timeout = {
let config_lock = config.lock().unwrap();
config_lock.get_setting("agent_timeout").cloned()
};
// Access agent registry
let registry = AgentRegistry::instance();
{
let mut registry_lock = registry.lock().unwrap();
registry_lock.register_agent("agent_1".to_string(), "analysis".to_string());
}Benefits
- Global Access: Single point of access to shared resources
- Resource Control: Prevent multiple instances of expensive resources
- Consistency: Maintain consistent state across the system
- Coordination: Central coordination point for system-wide operations
Use Cases
- Configuration management for AI systems
- Agent registry and discovery service
- Database connection pools
- Logging and monitoring services
Considerations
- Use sparingly as it can create tight coupling
- Consider dependency injection as an alternative
- Ensure thread safety in concurrent environments
- Be mindful of testing challenges with global state