Decorator Pattern
The Decorator Pattern adds new functionality to AI agents dynamically without altering their structure, enabling flexible behavior composition.
Pattern Overview
This pattern attaches additional responsibilities to agents dynamically, providing a flexible alternative to subclassing for extending functionality.
Structure
// Base agent trait
pub trait Agent {
fn process(&self, input: &str) -> String;
}
// Concrete base agent
pub struct BasicAgent {
name: String,
}
impl BasicAgent {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
}
impl Agent for BasicAgent {
fn process(&self, input: &str) -> String {
format!("Basic processing of: {}", input)
}
}
// Base decorator
pub trait AgentDecorator: Agent {
fn get_wrapped_agent(&self) -> &dyn Agent;
}
// Logging decorator
pub struct LoggingDecorator {
wrapped: Box<dyn Agent>,
}
impl LoggingDecorator {
pub fn new(agent: Box<dyn Agent>) -> Self {
Self { wrapped: agent }
}
}
impl Agent for LoggingDecorator {
fn process(&self, input: &str) -> String {
println!("LOG: Processing input: {}", input);
let result = self.wrapped.process(input);
println!("LOG: Result: {}", result);
result
}
}
// Validation decorator
pub struct ValidationDecorator {
wrapped: Box<dyn Agent>,
}
impl ValidationDecorator {
pub fn new(agent: Box<dyn Agent>) -> Self {
Self { wrapped: agent }
}
fn validate_input(&self, input: &str) -> bool {
!input.is_empty() && input.len() < 1000
}
}
impl Agent for ValidationDecorator {
fn process(&self, input: &str) -> String {
if self.validate_input(input) {
self.wrapped.process(input)
} else {
"Error: Invalid input".to_string()
}
}
}
// Caching decorator
pub struct CachingDecorator {
wrapped: Box<dyn Agent>,
// Simplified cache representation
cache_enabled: bool,
}
impl CachingDecorator {
pub fn new(agent: Box<dyn Agent>) -> Self {
Self {
wrapped: agent,
cache_enabled: true,
}
}
}
impl Agent for CachingDecorator {
fn process(&self, input: &str) -> String {
if self.cache_enabled {
// Simplified cache check
format!("CACHED: {}", self.wrapped.process(input))
} else {
self.wrapped.process(input)
}
}
}Usage Example
// Create base agent
let base_agent = BasicAgent::new("ProcessorAgent");
// Wrap with decorators
let logged_agent = LoggingDecorator::new(Box::new(base_agent));
let validated_agent = ValidationDecorator::new(Box::new(logged_agent));
let cached_agent = CachingDecorator::new(Box::new(validated_agent));
// Use the decorated agent
let result = cached_agent.process("sample data");Benefits
- Flexibility: Add/remove behaviors dynamically
- Composition: Combine multiple decorators
- Single Responsibility: Each decorator has one purpose
- Open/Closed: Open for extension, closed for modification
Use Cases
- Adding logging, validation, and caching to agents
- Implementing security and authentication layers
- Performance monitoring and metrics collection
- A/B testing different agent behaviors