130
GOF behavioral

Observer

Observer — class diagram
Plate 130 class diagram

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 implementations
Specimen 130.01 Dart View specimen ↗

The 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
}