Welcome to CodeYourCraft's in-depth guide on the Observer Pattern in Rust! This tutorial is designed for both beginners and intermediates, covering the basics, and diving deep into practical applications. Let's get started!
The Observer Pattern is a design pattern that allows objects to be notified when a change occurs in another object, without the need for the two objects to have direct knowledge of each other. It's a powerful tool for implementing event-driven architectures, where multiple entities can react to changes in a single entity.
Subject trait and its implementations for this purpose.Observer trait and implement it for specific use cases.Let's dive into an example where we'll create a simple weather application.
pub trait Subject {
type Observer: Observer;
fn register_observer(&mut self, observer: Self::Observer) -> &mut Self;
fn remove_observer(&mut self, observer: Self::Observer) -> &mut Self;
fn notify(&self, weather: &str);
}
pub trait Observer {
fn update(&mut self, weather: &str);
}Now, let's implement our WeatherData struct that will act as our subject.
pub struct WeatherData {
temperature: f32,
humidity: f32,
pressure: f32,
observers: Vec<Box<dyn Observer>>,
}
impl Subject for WeatherData {
type Observer = Box<dyn Observer>;
fn register_observer(&mut self, observer: Self::Observer) -> &mut Self {
self.observers.push(observer);
self
}
fn remove_observer(&mut self, observer: Self::Observer) -> &mut Self {
self.observers.retain(|o| o != observer);
self
}
fn notify(&self, weather: &str) {
for observer in &self.observers {
observer.update(weather);
}
}
}Let's create two observers: CurrentConditionsDisplay and StatisticsDisplay.
pub struct CurrentConditionsDisplay {
subject: Box<dyn Subject>,
temperature: f32,
humidity: f32,
pressure: f32,
}
impl Observer for CurrentConditionsDisplay {
fn update(&mut self, weather: &str) {
let values: Vec<&str> = weather.split(' ').collect();
self.temperature = values[1].parse::<f32>().unwrap();
self.humidity = values[3].parse::<f32>().unwrap();
self.pressure = values[5].parse::<f32>().unwrap();
self.display();
}
fn display(&self) {
println!("Current conditions: {}", self.temperature);
}
}
pub struct StatisticsDisplay {
subject: Box<dyn Subject>,
}
impl Observer for StatisticsDisplay {
fn update(&mut self, weather: &str) {
let values: Vec<&str> = weather.split(' ').collect();
self.process_stats(values[1].parse::<f32>().unwrap());
}
fn process_stats(&self, temp: f32) {
let max: f32 = self.get_max();
let min: f32 = self.get_min();
let avg: f32 = (self.get_sum() / (self.get_num() as f32)) as f32;
println!(
"Avg/Max/Min temperature: {:.1}/{:.1}/{:.1}",
avg, max, min
);
}
fn get_sum(&self) -> f32 {
0.0
}
fn get_num(&self) -> usize {
0
}
}Now, we can run our example and observe the behavior of our Observer Pattern implementation.
fn main() {
let subject = Box::new(WeatherData::new());
let current_display = Box::new(CurrentConditionsDisplay { subject });
let stats_display = Box::new(StatisticsDisplay { subject });
subject.register_observer(current_display);
subject.register_observer(stats_display);
subject.notify("Temperature: 30 Humidity: 65 Pressure: 1013");
subject.notify("Temperature: 28 Humidity: 60 Pressure: 1014");
}What is the Observer Pattern used for?
What are the roles of Subject and Observer in the Observer Pattern?