Observer
Oberver Pattern enables the broadcast communication between an event subject, that emits changes to its state to all of its observers.
Usage
Modern day usage is biased by the existence of rx and the reactive libraries across most important languages. Most modern libraries rely on observables and observers to get the most out of streams.
Examples
The best example both usage and implementation is http://reactivex.io/. In Angular it is very common for data binding matters.
Specimens
15 implementationsThe Observer pattern defines a one-to-many dependency between objects. A subject (observable) maintains a list of its dependents (observers) and notifies them of any state changes. This promotes loose coupling, allowing the subject to change without affecting observers, and vice-versa.
The Dart implementation uses streams (StreamController) as the observable subject and stream subscriptions as the observers. Each time the subject’s state changes, it emits a new value to the stream, automatically notifying all subscribers. Dart’s asynchronous stream handling makes this a natural and efficient fit for the observer pattern’s event-based nature. Using StreamController combined with Stream.listen is highly idiomatic for reactive programming in Dart.
// observer.dart
import 'dart:async';
// Subject (Observable)
class Subject {
final StreamController<int> _controller = StreamController<int>();
Stream<int> get stream => _controller.stream;
void set value(int newValue) {
_controller.sink.add(newValue);
}
void dispose() {
_controller.close();
}
}
// Observer
class Observer {
final String name;
void Function(int) _callback;
Observer(this.name, this. _callback);
factory Observer.withFunction(String name, void Function(int) callback) {
return Observer(name, callback);
}
void observe(Subject subject) {
subject.stream.listen(_callback);
}
}
void main() {
final subject = Subject();
final observer1 = Observer('Observer 1', (data) => print('$name received: $data'));
final observer2 = Observer('Observer 2', (data) => print('$name received: $data'));
observer1.observe(subject);
observer2.observe(subject);
subject.value = 1;
subject.value = 2;
subject.value = 3;
subject.dispose(); // Important to free resources
}
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Here, we implement it using Scala’s functional approach with Observable holding a collection of Observer function types. When the Observable’s state changes (e.g., via a notifyObservers method), it iterates through the observers and invokes each one with the new state. This leverages Scala’s first-class functions and immutability for a clean, concise, and type-safe implementation, avoiding mutable state where possible.
// Observable.scala
trait Observable[T] {
private var observers: List[T => Unit] = List.empty
def addObserver(observer: T => Unit): Unit = {
observers = observer :: observers
}
def removeObserver(observer: T => Unit): Unit = {
observers = observers.filter(_ != observer)
}
def notifyObservers(newState: T): Unit = {
observers.foreach(_(newState))
}
}
// Example Usage
case class SensorData(temperature: Double, humidity: Double)
object SensorExample {
def main(args: Array[String]): Unit = {
val sensor = new Observable[SensorData]
val display1 = (data: SensorData) => println(s"Display 1: Temperature = ${data.temperature}, Humidity = ${data.humidity}")
val display2 = (data: SensorData) => println(s"Display 2: Temperature = ${data.temperature}")
sensor.addObserver(display1)
sensor.addObserver(display2)
sensor.notifyObservers(SensorData(25.0, 60.0))
sensor.notifyObservers(SensorData(27.5, 62.5))
sensor.removeObserver(display2)
sensor.notifyObservers(SensorData(30.0, 70.0))
}
}
The Observer pattern defines a one-to-many dependency between objects. When the state of an object (the subject) changes, all its dependent objects (the observers) are notified and updated automatically. This promotes loose coupling, allowing subjects and observers to change independently.
The code implements this with a Subject class managing a list of Observer interfaces. When the subject’s data changes (using setState), it iterates through the observers and calls their update method, passing the new state. The observer interface ensures all observers have a consistent update method. This is idiomatic PHP due to its reliance on interfaces for defining contracts and the use of splitted array iteration for notifying observers.
<?php
/**
* Observer Interface
*/
interface Observer
{
public function update(string $data): void;
}
/**
* Subject Class - Maintains the state of data and notifies observers
*/
class Subject
{
private string $data;
private array $observers = [];
public function __construct(string $initialData = "")
{
$this->data = $initialData;
}
public function attach(Observer $observer): void
{
$this->observers[] = $observer;
}
public function detach(Observer $observer): void
{
$key = array_search($observer, $this->observers, true);
if ($key !== false) {
unset($this->observers[$key]);
$this->observers = array_values($this->observers); // Re-index array
}
}
public function setState(string $data): void
{
$this->data = $data;
$this->notify();
}
public function getState(): string
{
return $this->data;
}
private function notify(): void
{
foreach ($this->observers as $observer) {
$observer->update($this->data);
}
}
}
/**
* Concrete Observer 1
*/
class ConcreteObserver1 implements Observer
{
public function update(string $data): void
{
echo "ConcreteObserver1: Reacted to the state change: " . $data . PHP_EOL;
}
}
/**
* Concrete Observer 2
*/
class ConcreteObserver2 implements Observer
{
public function update(string $data): void
{
echo "ConcreteObserver2: Received update: " . $data . PHP_EOL;
}
}
// Usage
$subject = new Subject('Initial State');
$observer1 = new ConcreteObserver1();
$subject->attach($observer1);
$observer2 = new ConcreteObserver2();
$subject->attach($observer2);
$subject->setState('New State');
$subject->setState('Another State');
$subject->detach($observer2);
$subject->setState('Final State');
?>
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. A subject (observable) maintains a list of observers and notifies them of any state changes. This promotes loose coupling as subjects don’t need to know concrete observer classes.
This Ruby implementation utilizes the Observer module from the standard library, providing a clean and concise way to establish the observation relationship. Observers register themselves with the subject using observe. The subject then calls notify_observers with the changed data, which is passed to each observer’s update method. Ruby’s flexibility allows for simple and readable observer registration and notification. The use of a hash to store observers is a common Ruby practice.
module Subject
def observe(observer)
@observers ||= []
@observers << observer
end
def notify_observers(data)
@observers&.each { |observer| observer.update(data) }
end
end
class Data
include Subject
attr_accessor :value
def initialize(value)
@value = value
end
def set_value(new_value)
@value = new_value
notify_observers(@value)
end
end
class ConcreteObserver
def update(data)
puts "Observer received update: #{data}"
end
end
# Example Usage
data = Data.new(10)
observer1 = ConcreteObserver.new
observer2 = ConcreteObserver.new
data.observe(observer1)
data.observe(observer2)
data.set_value(20)
data.set_value(30)
The Observer pattern defines a one-to-many dependency between objects. When the state of one object (the subject) changes, all its dependent objects (the observers) are notified and updated automatically. This promotes loose coupling, allowing subjects and observers to interact without knowing each other’s concrete classes.
The Swift code below uses closures as observers. A Subject class maintains a list of observers (closures) and notifys them when its state changes. The Observer struct simply holds a name for identification and prints a message when updated. Swift’s concise syntax and first-class function support make closure-based observers a common and idiomatic approach for this pattern, avoiding the need for explicit protocol conformance in many cases.
// Observer.swift
struct Observer {
let name: String
func update(state: Int) {
print("\(name) received update: \(state)")
}
}
class Subject {
private var state: Int = 0
private var observers: [(Observer) -> Void] = []
func attach(observer: @escaping (Observer) -> Void) {
observers.append(observer)
}
func detach(observer: @escaping (Observer) -> Void) {
observers = observers.filter { $0 !== observer }
}
func setState(newState: Int) {
state = newState
notify()
}
private func notify() {
for observer in observers {
observer(Observer(name: "Observer")) // a simplified observer
}
}
}
// Example Usage:
let subject = Subject()
subject.attach { observer in
observer.update(state: subject.state)
}
subject.setState(newState: 1)
subject.setState(newState: 2)
The Observer pattern defines a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically. This promotes loose coupling and improved modularity. Here, Subject maintains a list of Observers and notifies them when its data changes. The ConcreteSubject holds the data and notifies observers. Observer defines an update method, and ConcreteObserver implements this to react to the subject’s changes. Kotlin’s support for extension functions and first-class functions allows for a concise and flexible implementation. The use of interfaces promotes abstraction.
// Observer Pattern in Kotlin
// Observer Interface
interface Observer {
fun update(data: String)
}
// Subject Interface
interface Subject {
fun registerObserver(observer: Observer)
fun unregisterObserver(observer: Observer)
fun notifyObservers(data: String)
}
// Concrete Subject
class ConcreteSubject : Subject {
private val observers = mutableListOf<Observer>()
private var data: String = "Initial Data"
override fun registerObserver(observer: Observer) {
observers.add(observer)
}
override fun unregisterObserver(observer: Observer) {
observers.remove(observer)
}
override fun notifyObservers(data: String) {
this.data = data
observers.forEach { it.update(data) }
}
fun getData(): String = data
}
// Concrete Observer
class ConcreteObserver(private val id: Int) : Observer {
override fun update(data: String) {
println("Observer $id: Data updated to: $data")
}
}
// Example Usage
fun main() {
val subject = ConcreteSubject()
val observer1 = ConcreteObserver(1)
val observer2 = ConcreteObserver(2)
subject.registerObserver(observer1)
subject.registerObserver(observer2)
subject.notifyObservers("New Data 1")
subject.notifyObservers("New Data 2")
subject.unregisterObserver(observer1)
subject.notifyObservers("Final Data")
}
The Observer pattern defines a one-to-many dependency between objects. A subject maintains a list of its dependents, called observers, and automatically notifies them of any state changes. This promotes loose coupling; the subject doesn’t need to know concrete observer classes, only that they implement a common interface.
This Rust implementation utilizes the Arc and Mutex to allow safe sharing of the subject’s state between threads (observers could potentially be in different threads). We define traits for Subject and Observer. The Subject holds a vector of Observer trait objects and notifies them when its state changes, using a callback through the notify_observers method. The code employs Rust’s ownership and borrowing rules, and Arc with Mutex to safely handle the shared state, showcasing idiomatic Rust concurrency.
use std::sync::{Arc, Mutex};
use std::cell::RefCell;
// Define the Observer trait
trait Observer {
fn update(&self, message: &str);
}
// Define the Subject trait
trait Subject {
fn attach(&mut self, observer: &dyn Observer);
fn detach(&mut self, observer: &dyn Observer);
fn notify_observers(&self, message: &str);
}
// Concrete Subject
struct NewsAgency {
observers: RefCell<Vec<Box<dyn Observer>>>,
news: Arc<Mutex<String>>,
}
impl NewsAgency {
fn new() -> Self {
NewsAgency {
observers: RefCell::new(Vec::new()),
news: Arc::new(Mutex::new(String::from("Initial News"))),
}
}
}
impl Subject for NewsAgency {
fn attach(&mut self, observer: &dyn Observer) {
self.observers.borrow_mut().push(Box::new(observer.clone())); // Clone to satisfy ownership
}
fn detach(&mut self, observer: &dyn Observer) {
self.observers.borrow_mut().retain(|o| {
let ptr1 = o.as_ref() as *const dyn Observer;
let ptr2 = observer as *const dyn Observer;
ptr1 != ptr2
});
}
fn notify_observers(&self, message: &str) {
let news = self.news.lock().unwrap();
for observer in self.observers.borrow().iter() {
observer.update(&format!("Breaking News: {} - {}", message, news));
}
}
}
// Concrete Observer
struct Subscriber {
name: String,
}
impl Subscriber {
fn new(name: String) -> Self {
Subscriber { name }
}
}
impl Observer for Subscriber {
fn update(&self, message: &str) {
println!("{} received update: {}", self.name, message);
}
}
// Clone implementation to satisfy the trait bound in attach
impl Clone for Subscriber {
fn clone(&self) -> Self {
Subscriber{name: self.name.clone()}
}
}
fn main() {
let agency = NewsAgency::new();
let subscriber1 = Subscriber::new(String::from("Alice"));
let subscriber2 = Subscriber::new(String::from("Bob"));
agency.attach(&subscriber1);
agency.attach(&subscriber2);
agency.notify_observers("The stock market crashed!");
agency.detach(&subscriber1);
agency.notify_observers("The market recovered slightly.");
}
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. It promotes loose coupling as the subject doesn’t need to know concrete observer details.
This Go implementation uses interfaces to define the Subject and Observer. The Subject maintains a list of Observers and calls a Update() method on each when its state changes. The Observer interface has a single Update() method that receives the subject’s data. Concrete subject and observers implement these interfaces. The use of interfaces and function types aligns well with Go’s emphasis on composition and decoupling. Channels are not used because that adds unnecessary complexity for this basic implementation, deferring that to more concurrent and complex real-world scenarios.
package main
import "fmt"
// Observer interface
type Observer interface {
Update(data int)
}
// Subject interface
type Subject interface {
Register(observer Observer)
Deregister(observer Observer)
Notify()
}
// Concrete Subject
type ConcreteSubject struct {
observers []Observer
data int
}
func (s *ConcreteSubject) Register(observer Observer) {
s.observers = append(s.observers, observer)
}
func (s *ConcreteSubject) Deregister(observer Observer) {
for i, o := range s.observers {
if o == observer {
s.observers = append(s.observers[:i], s.observers[i+1:]...)
return
}
}
}
func (s *ConcreteSubject) Notify() {
for _, observer := range s.observers {
observer.Update(s.data)
}
}
func (s *ConcreteSubject) SetData(newData int) {
s.data = newData
s.Notify()
}
// Concrete Observers
type ConcreteObserverA struct {
id int
}
func (o *ConcreteObserverA) Update(data int) {
fmt.Printf("Observer A %d received update: %d\n", o.id, data)
}
type ConcreteObserverB struct {
id int
}
func (o *ConcreteObserverB) Update(data int) {
fmt.Printf("Observer B %d received update: %d\n", o.id, data)
}
func main() {
subject := &ConcreteSubject{}
observerA1 := &ConcreteObserverA{id: 1}
observerA2 := &ConcreteObserverA{id: 2}
observerB1 := &ConcreteObserverB{id: 1}
subject.Register(observerA1)
subject.Register(observerA2)
subject.Register(observerB1)
subject.SetData(10)
subject.SetData(20)
subject.Deregister(observerA2)
subject.SetData(30)
}
The Observer pattern defines a one-to-many dependency between objects. When one object (the subject) changes state, all its dependents (observers) are notified and updated automatically. This promotes loose coupling as subjects are unaware of specific observers. The code uses function pointers to represent observers, and a vector to store them. The notify_observers function iterates through this vector, calling each observer function with the updated data. This approach is common in C to achieve event handling or callback mechanisms without complex object hierarchies, aligning with its procedural nature.
#include <stdio.h>
#include <stdlib.h>
#include <vector.h>
// Define Observer function pointer type
typedef void (*observer_func)(int);
// Structure for the Subject
typedef struct {
int state;
vector_t *observers;
} Subject;
// Function to create a new subject
Subject* subject_create() {
Subject *sub = (Subject*)malloc(sizeof(Subject));
sub->state = 0;
sub->observers = vector_create(sizeof(observer_func));
return sub;
}
// Function to attach an observer
void subject_attach(Subject *sub, observer_func observer) {
vector_push_back(sub->observers, &observer);
}
// Function to detach an observer
void subject_detach(Subject *sub, observer_func observer) {
for (size_t i = 0; i < vector_size(sub->observers); ++i) {
if (*(observer_func*)vector_at(sub->observers, i) == observer) {
vector_erase(sub->observers, i);
return;
}
}
}
// Function to set the subject's state and notify observers.
void subject_set_state(Subject *sub, int new_state) {
sub->state = new_state;
for (size_t i = 0; i < vector_size(sub->observers); ++i) {
observer_func observer = *(observer_func*)vector_at(sub->observers, i);
observer(new_state);
}
}
// Example observer functions
void observer1(int state) {
printf("Observer 1: State changed to %d\n", state);
}
void observer2(int state) {
printf("Observer 2: State changed to %d\n", state);
}
// Main function (example usage)
int main() {
Subject *subject = subject_create();
subject_attach(subject, observer1);
subject_attach(subject, observer2);
subject_set_state(subject, 10);
subject_set_state(subject, 20);
subject_detach(subject, observer1);
subject_set_state(subject, 30);
vector_destroy(subject->observers);
free(subject);
return 0;
}
The Observer pattern defines a one-to-many dependency between objects so that when one object (the subject) changes state, all its dependents (observers) are notified and updated automatically. This promotes loose coupling. Here, we use C++’s standard std::function for the observer’s update method, allowing flexible observer types. The Subject broadcasts updates to registered observers by calling their update functions. This implementation is idiomatic because it leverages C++’s function objects and standard library features for a concise and type-safe solution, avoiding explicit interfaces where not strictly necessary, although a base class interface is also viable.
#include <iostream>
#include <vector>
#include <functional>
class Subject {
public:
using ObserverCallback = std::function<void(int)>;
void attach(ObserverCallback observer) {
observers_.push_back(observer);
}
void detach(ObserverCallback observer) {
for (auto it = observers_.begin(); it != observers_.end(); ++it) {
if (*it == observer) {
observers_.erase(it);
break;
}
}
}
void set_state(int state) {
state_ = state;
notify();
}
int get_state() const {
return state_;
}
private:
void notify() {
for (const auto& observer : observers_) {
observer(state_);
}
}
int state_ = 0;
std::vector<ObserverCallback> observers_;
};
int main() {
Subject subject;
// Observer 1 (using a lambda)
subject.attach([](int state) {
std::cout << "Observer 1: State changed to " << state << std::endl;
});
// Observer 2 (using a function)
auto observer2_func = [](int state) {
std::cout << "Observer 2: State is now " << state * 2 << std::endl;
};
subject.attach(observer2_func);
subject.set_state(1);
subject.set_state(2);
subject.detach(observer2_func);
subject.set_state(3);
return 0;
}
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Here, an ISubject interface declares the Attach, Detach, and Notify methods for managing observers. A concrete Subject publishes events containing data. Multiple Observer classes ConcreteObserverA and ConcreteObserverB subscribe to these events and respond to changes in the subject’s state. This implementation uses C#’s events and delegates for a type-safe and concise way to manage the observer relationships, fitting the language’s event-driven programming style.
// ISubject interface
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
// Observer interface
public interface IObserver
{
void Update(string data);
}
// Subject class
public class Subject : ISubject
{
private readonly List<IObserver> _observers = new();
private string _state;
public string State
{
get { return _state; }
set
{
_state = value;
Notify();
}
}
public void Attach(IObserver observer)
{
_observers.Add(observer);
}
public void Detach(IObserver observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in _observers)
{
observer.Update(_state);
}
}
}
// Concrete Observer A
public class ConcreteObserverA : IObserver
{
public void Update(string data)
{
Console.WriteLine($"Observer A: Received update - {data}");
}
}
// Concrete Observer B
public class ConcreteObserverB : IObserver
{
public void Update(string data)
{
Console.WriteLine($"Observer B: Received update - {data.ToUpper()}");
}
}
// Usage
public class Example
{
public static void Main(string[] args)
{
var subject = new Subject();
var observerA = new ConcreteObserverA();
var observerB = new ConcreteObserverB();
subject.Attach(observerA);
subject.Attach(observerB);
subject.State = "Initial state";
subject.State = "Another state";
subject.Detach(observerA);
subject.State = "Final state";
}
}
The Observer pattern defines a one-to-many dependency between objects. A subject (observable) maintains a list of observers (dependents), and notifies them of any state changes. This promotes loose coupling, allowing subjects and observers to change independently.
This TypeScript implementation uses classes and interfaces to define the Subject and Observer roles. The Subject manages a list of Observers and provides methods to attach, detach, and notify them. Observers subscribe to the subject to receive updates. The use of interfaces (Observer, Subject) enforces contracts and promotes type safety, common TypeScript practice. The notification is done via a callback function (the update method) allowing observers to react to the change without knowing the subject’s internals. This pattern naturally fits TypeScript’s class-based structure and preference for type annotations.
// Observer Interface
interface Observer {
update(data: any): void;
}
// Subject Interface
interface Subject {
register(observer: Observer): void;
unregister(observer: Observer): void;
notify(): void;
}
// Concrete Subject
class DataSubject implements Subject {
private observers: Observer[] = [];
private data: any;
setData(newData: any): void {
this.data = newData;
this.notify();
}
getData(): any {
return this.data;
}
register(observer: Observer): void {
this.observers.push(observer);
}
unregister(observer: Observer): void {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(): void {
this.observers.forEach(observer => observer.update(this.data));
}
}
// Concrete Observer
class DataLogger implements Observer {
update(data: any): void {
console.log(`Data updated: ${data}`);
}
}
class DataDisplay implements Observer {
update(data: any): void {
document.getElementById('dataDisplay')?. (textContent => `Displayed Data: ${data}`)(document.getElementById('dataDisplay').textContent);
}
}
// Usage
const subject = new DataSubject();
const logger = new DataLogger();
const display = new DataDisplay();
subject.register(logger);
subject.register(display);
subject.setData("Hello, Observer!");
subject.setData(123);
subject.unregister(logger);
subject.setData("Goodbye, Logger!");
The Observer pattern defines a one-to-many dependency between objects. A subject (observable) maintains a list of its dependent observers and automatically notifies them of any state changes. This promotes loose coupling, as observers don’t need to know the subject’s specifics, only how to react to changes.
This JavaScript implementation uses a simple array to store observers. The Subject class has methods to subscribe, unsubscribe, and notify. When the subject’s data changes (via setData), it iterates through the observers and calls their update methods. This leverages JavaScript’s first-class functions, making it easy to add and remove observer functions. It is a common and efficient approach for implementing event handling and reactive systems in JavaScript.
/**
* Observer Pattern in JavaScript
*/
class Subject {
constructor() {
this.observers = [];
this.data = null;
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
setData(newData) {
this.data = newData;
this.notify();
}
getData() {
return this.data;
}
notify() {
this.observers.forEach(observer => observer.update(this.data));
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received update: ${data}`);
}
}
// Usage
const subject = new Subject();
const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");
subject.subscribe(observer1);
subject.subscribe(observer2);
subject.setData("Initial Data");
subject.setData("New Data");
subject.unsubscribe(observer1);
subject.setData("Even Newer Data");
The Observer pattern defines a one-to-many dependency between objects so that when one object (the subject) changes state, all its dependents (observers) are notified and updated automatically. This promotes loose coupling, allowing subjects and observers to evolve independently.
The code implements the pattern with a Subject class that maintains a list of Observer objects. Observers register with the subject and define an update method to handle notifications. The Subject’s attach, detach, and notify methods manage the observer list and dispatch updates. This uses Python’s dynamic typing and first-class functions, fitting the language’s flexible nature, and leverages lists for observer storage—a common and direct approach in Python.
# Observer Pattern in Python
class Observer:
def update(self, subject):
raise NotImplementedError
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def notify(self, data):
for observer in self._observers:
observer.update(data)
class ConcreteObserver(Observer):
def __init__(self, name):
self.name = name
def update(self, subject):
print(f"{self.name} received update: {subject}")
class DataStore(Subject):
def __init__(self):
super().__init__()
self._data = None
def set_data(self, data):
self._data = data
self.notify(data)
def get_data(self):
return self._data
if __name__ == "__main__":
data_store = DataStore()
observer1 = ConcreteObserver("Observer 1")
observer2 = ConcreteObserver("Observer 2")
data_store.attach(observer1)
data_store.attach(observer2)
data_store.set_data("Initial Data")
data_store.set_data("Updated Data")
data_store.detach(observer2)
data_store.set_data("Final Data")
The Observer pattern defines a one-to-many dependency between objects. When the state of one object (the Subject) changes, all its dependent objects (the Observers) are notified and updated automatically. This promotes loose coupling, as the Subject doesn’t need to know specific Observer details.
The code demonstrates this with a Subject (WeatherData) that maintains weather data and a list of Observers (DisplayElements). When the weather data changes (temperature, humidity, pressure), the Subject iterates through its registered Observers and calls their update() methods, passing the new data. This is implemented using Java interfaces for Observer and Subject, facilitating flexibility and allowing different displays to react to the same data source without modification to the source. This approach adheres to Java’s principles of interface-based programming and promotes maintainability.
import java.util.ArrayList;
import java.util.List;
// Observer Interface
interface Observer {
void update(float temperature, float humidity, float pressure);
}
// Subject Interface
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
float getTemperature();
float getHumidity();
float getPressure();
}
// Concrete Subject
class WeatherData implements Subject {
private List<Observer> observers = new ArrayList<>();
private float temperature;
private float humidity;
private float pressure;
public void setWeatherData(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
// Concrete Observers
class CurrentConditionsDisplay implements Observer {
private float temperature;
private float humidity;
private float pressure;
private WeatherData weatherData;
public CurrentConditionsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity, " + pressure + " pressure");
}
}
class ForecastDisplay implements Observer {
private float temperature;
private float humidity;
private float pressure;
private WeatherData weatherData;
public ForecastDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("Forecast: Temperature will be " + temperature + "F, humidity " + humidity + "%");
}
}
// Main Class (for testing)
public class ObserverExample {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(weatherData);
ForecastDisplay forecastConditions = new ForecastDisplay(weatherData);
weatherData.setWeatherData(80, 60, 1013);
weatherData.setWeatherData(82, 70, 1015);
weatherData.removeObserver(forecastConditions);
weatherData.setWeatherData(83, 65, 1014);
}
}