Observer pattern

Last updated

In software design and engineering, the observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

Contents

It is often used for implementing distributed event-handling systems in event-driven software. In such systems, the subject is usually named a "stream of events" or "stream source of events" while the observers are called "sinks of events." The stream nomenclature alludes to a physical setup in which the observers are physically separated and have no control over the emitted events from the subject/stream source. This pattern thus suits any process by which data arrives from some input that is not available to the CPU at startup, but instead arrives seemingly at random (HTTP requests, GPIO data, user input from peripherals and distributed databases, etc.).

Overview

The observer design pattern is a behavioural pattern listed among the 23 well-known "Gang of Four" design patterns that address recurring design challenges in order to design flexible and reusable object-oriented software, yielding objects that are easier to implement, change, test and reuse. [1]

The observer pattern addresses the following problems: [2]

Defining a one-to-many dependency between objects by defining one object (subject) that updates the state of dependent objects directly is inflexible because it couples the subject to particular dependent objects. However, it might be applicable from a performance point of view or if the object implementation is tightly coupled (such as low-level kernel structures that execute thousands of times per second). Tightly coupled objects can be difficult to implement in some scenarios and are not easily reused because they refer to and are aware of many objects with different interfaces. In other scenarios, tightly coupled objects can be a better option because the compiler is able to detect errors at compile time and optimize the code at the CPU instruction level.

The sole responsibility of a subject is to maintain a list of observers and to notify them of state changes by calling their update() operation. The responsibility of observers is to register and unregister themselves with a subject (in order to be notified of state changes) and to update their state (to synchronize their state with the subject's state) when they are notified. This makes subject and observers loosely coupled. Subject and observers have no explicit knowledge of each other. Observers can be added and removed independently at run time. This notification-registration interaction is also known as publish-subscribe.

Strong vs. weak reference

The observer pattern can cause memory leaks, known as the lapsed listener problem, because in a basic implementation, it requires both explicit registration and explicit deregistration, as in the dispose pattern, because the subject holds strong references to the observers, keeping them alive. This can be prevented if the subject holds weak references to the observers.

Coupling and typical publish-subscribe implementations

Typically, the observer pattern is implemented so that the subject being observed is part of the object for which state changes are being observed (and communicated to the observers). This type of implementation is considered tightly coupled, forcing both the observers and the subject to be aware of each other and have access to their internal parts, creating possible issues of scalability, speed, message recovery and maintenance (also called event or notification loss), the lack of flexibility in conditional dispersion and possible hindrance to desired security measures. In some (non-polling) implementations of the publish-subscribe pattern, this is solved by creating a dedicated message queue server (and sometimes an extra message handler object) as an extra stage between the observer and the object being observed, thus decoupling the components. In these cases, the message queue server is accessed by the observers with the observer pattern, subscribing to certain messages and knowing (or not knowing, in some cases) about only the expected message, while knowing nothing about the message sender itself; the sender may also know nothing about the observers. Other implementations of the publish-subscribe pattern, which achieve a similar effect of notification and communication to interested parties, do not use the observer pattern. [3] [4]

In early implementations of multi-window operating systems such as OS/2 and Windows, the terms "publish-subscribe pattern" and "event-driven software development" were used as synonyms for the observer pattern. [5]

The observer pattern, as described in the Design Patterns book, is a very basic concept and does not address removing interest in changes to the observed subject or special logic to be performed by the observed subject before or after notifying the observers. The pattern also does not deal with recording change notifications or guaranteeing that they are received. These concerns are typically handled in message-queueing systems, in which the observer pattern plays only a small part.

Related patterns include publish–subscribe, mediator and singleton.

Uncoupled

The observer pattern may be used in the absence of publish-subscribe, as when model status is frequently updated. Frequent updates may cause the view to become unresponsive (e.g., by invoking many repaint calls); such observers should instead use a timer. Instead of becoming overloaded by change message, the observer will cause the view to represent the approximate state of the model at a regular interval. This mode of observer is particularly useful for progress bars, in which the underlying operation's progress changes frequently.

Structure

UML class and sequence diagram

A sample UML class and sequence diagram for the observer design pattern. W3sDesign Observer Design Pattern UML.jpg
A sample UML class and sequence diagram for the observer design pattern.

In this UML class diagram, the Subject class does not update the state of dependent objects directly. Instead, Subject refers to the Observer interface (update()) for updating state, which makes the Subject independent of how the state of dependent objects is updated. The Observer1 and Observer2 classes implement the Observer interface by synchronizing their state with subject's state.

The UML sequence diagram shows the runtime interactions: The Observer1 and Observer2 objects call attach(this) on Subject1 to register themselves. Assuming that the state of Subject1 changes, Subject1 calls notify() on itself. notify() calls update() on the registered Observer1 and Observer2objects, which request the changed data (getState()) from Subject1 to update (synchronize) their state.

UML class diagram

UML class diagram of Observer pattern Observer w update.svg
UML class diagram of Observer pattern

Example

While the library classes java.util.Observer and java.util.Observable exist, they have been deprecated in Java 9 because the model implemented was quite limited.

Below is an example written in Java that takes keyboard input and handles each input line as an event. When a string is supplied from System.in, the method notifyObservers() is then called in order to notify all observers of the event's occurrence, in the form of an invocation of their update methods.

Java

importjava.util.ArrayList;importjava.util.List;importjava.util.Scanner;interfaceObserver{voidupdate(Stringevent);}classEventSource{List<Observer>observers=newArrayList<>();publicvoidnotifyObservers(Stringevent){observers.forEach(observer->observer.update(event));}publicvoidaddObserver(Observerobserver){observers.add(observer);}publicvoidscanSystemIn(){Scannerscanner=newScanner(System.in);while(scanner.hasNextLine()){Stringline=scanner.nextLine();notifyObservers(line);}}}publicclassObserverDemo{publicstaticvoidmain(String[]args){System.out.println("Enter Text: ");EventSourceeventSource=newEventSource();eventSource.addObserver(event->System.out.println("Received response: "+event));eventSource.scanSystemIn();}}

C++

This is a C++11 implementation.

#include<functional>#include<iostream>#include<list>classSubject;//Forward declaration for usage in ObserverclassObserver{public:explicitObserver(Subject&subj);virtual~Observer();Observer(constObserver&)=delete;// rule of threeObserver&operator=(constObserver&)=delete;virtualvoidupdate(Subject&s)const=0;private:// Reference to a Subject object to detach in the destructorSubject&subject;};// Subject is the base class for event generationclassSubject{public:usingRefObserver=std::reference_wrapper<constObserver>;// Notify all the attached obseversvoidnotify(){for(constauto&x:observers){x.get().update(*this);}}// Add an observervoidattach(constObserver&observer){observers.push_front(observer);}// Remove an observervoiddetach(Observer&observer){observers.remove_if([&observer](constRefObserver&obj){return&obj.get()==&observer;});}private:std::list<RefObserver>observers;};Observer::Observer(Subject&subj):subject(subj){subject.attach(*this);}Observer::~Observer(){subject.detach(*this);}// Example of usageclassConcreteObserver:publicObserver{public:ConcreteObserver(Subject&subj):Observer(subj){}// Get notificationvoidupdate(Subject&)constoverride{std::cout<<"Got a notification"<<std::endl;}};intmain(){Subjectcs;ConcreteObserverco1(cs);ConcreteObserverco2(cs);cs.notify();}

The program output is like

GotanotificationGotanotification

Groovy

classEventSource{privateobservers=[]privatenotifyObservers(Stringevent){observers.each{it(event)}}voidaddObserver(observer){observers+=observer}voidscanSystemIn(){varscanner=newScanner(System.in)while(scanner){varline=scanner.nextLine()notifyObservers(line)}}}println'Enter Text: 'vareventSource=newEventSource()eventSource.addObserver{event->println"Received response: $event"}eventSource.scanSystemIn()

Kotlin

importjava.util.ScannertypealiasObserver=(event:String)->Unit;classEventSource{privatevarobservers=mutableListOf<Observer>()privatefunnotifyObservers(event:String){observers.forEach{it(event)}}funaddObserver(observer:Observer){observers+=observer}funscanSystemIn(){valscanner=Scanner(System.`in`)while(scanner.hasNext()){valline=scanner.nextLine()notifyObservers(line)}}}
funmain(arg:List<String>){println("Enter Text: ")valeventSource=EventSource()eventSource.addObserver{event->println("Received response: $event")}eventSource.scanSystemIn()}

Delphi

usesSystem.Generics.Collections,System.SysUtils;typeIObserver=interface['{0C8F4C5D-1898-4F24-91DA-63F1DD66A692}']procedureUpdate(constAValue:string);end;typeTObserverManager=classprivateFObservers:TList<IObserver>;publicconstructorCreate;overload;destructorDestroy;override;procedureNotifyObservers(constAValue:string);procedureAddObserver(constAObserver:IObserver);procedureUnregisterObsrver(constAObserver:IObserver);end;typeTListener=class(TInterfacedObject,IObserver)privateFName:string;publicconstructorCreate(constAName:string);reintroduce;procedureUpdate(constAValue:string);end;procedureTObserverManager.AddObserver(constAObserver:IObserver);beginifnotFObservers.Contains(AObserver)thenFObservers.Add(AObserver);end;beginFreeAndNil(FObservers);inherited;end;procedureTObserverManager.NotifyObservers(constAValue:string);vari:Integer;beginfori:=0toFObservers.Count-1doFObservers[i].Update(AValue);end;procedureTObserverManager.UnregisterObsrver(constAObserver:IObserver);beginifFObservers.Contains(AObserver)thenFObservers.Remove(AObserver);end;constructorTListener.Create(constAName:string);begininheritedCreate;FName:=AName;end;procedureTListener.Update(constAValue:string);beginWriteLn(FName+' listener received notification: '+AValue);end;procedureTMyForm.ObserverExampleButtonClick(Sender:TObject);varLDoorNotify:TObserverManager;LListenerHusband:IObserver;LListenerWife:IObserver;beginLDoorNotify:=TObserverManager.Create;tryLListenerHusband:=TListener.Create('Husband');LDoorNotify.AddObserver(LListenerHusband);LListenerWife:=TListener.Create('Wife');LDoorNotify.AddObserver(LListenerWife);LDoorNotify.NotifyObservers('Someone is knocking on the door');finallyFreeAndNil(LDoorNotify);end;end;

Output

Husband listener received notification: Someone is knocking on the door Wife listener received notification: Someone is knocking on the door 

Python

A similar example in Python:

classObservable:def__init__(self):self._observers=[]defregister_observer(self,observer)->None:self._observers.append(observer)defnotify_observers(self,*args,**kwargs)->None:forobserverinself._observers:observer.notify(self,*args,**kwargs)classObserver:def__init__(self,observable):observable.register_observer(self)defnotify(self,observable,*args,**kwargs)->None:print("Got",args,kwargs,"From",observable)subject=Observable()observer=Observer(subject)subject.notify_observers("test",kw="python")# prints: Got ('test',) {'kw': 'python'} From <__main__.Observable object at 0x0000019757826FD0>

C#

classPayload{internalstringMessage{get;set;}}classSubject:IObservable<Payload>{privatereadonlyICollection<IObserver<Payload>>_observers=newList<IObserver<Payload>>();IDisposableIObservable<Payload>.Subscribe(IObserver<Payload>observer){if(!_observers.Contains(observer)){_observers.Add(observer);}returnnewUnsubscriber(observer,_observers);}internalvoidSendMessage(stringmessage){foreach(varobserverin_observers){observer.OnNext(newPayload{Message=message});}}}internalclassUnsubscriber:IDisposable{privatereadonlyIObserver<Payload>_observer;privatereadonlyICollection<IObserver<Payload>>_observers;internalUnsubscriber(IObserver<Payload>observer,ICollection<IObserver<Payload>>observers){_observer=observer;_observers=observers;}voidIDisposable.Dispose(){if(_observer!=null&&_observers.Contains(_observer)){_observers.Remove(_observer);}}}internalclassObserver:IObserver<Payload>{internalstringMessage{get;set;}publicvoidOnCompleted(){}publicvoidOnError(Exceptionerror){}publicvoidOnNext(Payloadvalue){Message=value.Message;}internalIDisposableRegister(IObservable<Payload>subject){returnsubject.Subscribe(this);}}

JavaScript

JavaScript has a deprecated Object.observe function that was a more accurate implementation of the observer pattern. [7] This would fire events upon change to the observed object. Without the deprecated Object.observe function, the pattern may be implemented with more explicit code: [8]

letSubject={_state:0,_observers:[],add:function(observer){this._observers.push(observer);},getState:function(){returnthis._state;},setState:function(value){this._state=value;for(leti=0;i<this._observers.length;i++){this._observers[i].signal(this);}}};letObserver={signal:function(subject){letcurrentValue=subject.getState();console.log(currentValue);}}Subject.add(Observer);Subject.setState(10);//Output in console.log - 10

See also

Related Research Articles

A visitor pattern is a software design pattern that separates the algorithm from the object structure. Because of this separation new operations can be added to existing object structures without modifying the structures. It is one way to follow the open/closed principle in object-oriented programming and software engineering.

In computing, the Java Remote Method Invocation is a Java API that performs remote method invocation, the object-oriented equivalent of remote procedure calls (RPC), with support for direct transfer of serialized Java classes and distributed garbage-collection.

In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.

The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently", introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

The memento pattern is a software design pattern that exposes the private internal state of an object. One example of how this can be used is to restore an object to its previous state, another is versioning, another is custom serialization.

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.

In object-oriented (OO) and functional programming, an immutable object is an object whose state cannot be modified after it is created. This is in contrast to a mutable object, which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change, but the object's state appears unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.

In object-oriented programming languages, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".

In computing, type introspection is the ability of a program to examine the type or properties of an object at runtime. Some programming languages possess this capability.

In computer programming, a callback or callback function is any reference to executable code that is passed as an argument to another piece of code; that code is expected to call back (execute) the callback function as part of its job. This execution may be immediate as in a synchronous callback, or it might happen at a later point in time as in an asynchronous callback. They are also called blocking and non-blocking.

<span class="mw-page-title-main">Method overriding</span> Language feature in object-oriented programming

Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. In addition to providing data-driven algorithm-determined parameters across virtual network interfaces, it also allows for a specific type of polymorphism (subtyping). The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class. The version of a method that is executed will be determined by the object that is used to invoke it. If an object of a parent class is used to invoke the method, then the version in the parent class will be executed, but if an object of the subclass is used to invoke the method, then the version in the child class will be executed. This helps in preventing problems associated with differential relay analytics which would otherwise rely on a framework in which method overriding might be obviated. Some languages allow a programmer to prevent a method from being overridden.

<span class="mw-page-title-main">Java syntax</span> Set of rules defining correctly structured program

The syntax of Java is the set of rules defining how a Java program is written and interpreted.

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and Java packages may be annotated. Like Javadoc tags, Java annotations can be read from source files. Unlike Javadoc tags, Java annotations can also be embedded in and read from Java class files generated by the Java compiler. This allows annotations to be retained by the Java virtual machine at run-time and read via reflection. It is possible to create meta-annotations out of the existing ones in Java.

Event-driven architecture (EDA) is a software architecture paradigm concerning the production and detection of events.

In programming and software design, an event is an action or occurrence recognized by software, often originating asynchronously from the external environment, that may be handled by the software. Computer events can be generated or triggered by the system, by the user, or in other ways. Typically, events are handled synchronously with the program flow; that is, the software may have one or more dedicated places where events are handled, frequently an event loop.

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".

<span class="mw-page-title-main">ReactiveX</span> Program library

ReactiveX is a software library originally created by Microsoft that allows imperative programming languages to operate on sequences of data regardless of whether the data is synchronous or asynchronous. It provides a set of sequence operators that operate on each item in the sequence. It is an implementation of reactive programming and provides a blueprint for the tools to be implemented in multiple programming languages.

Apache Commons Logging is a Java-based logging utility and a programming model for logging and for other toolkits. It provides APIs, log implementations, and wrapper implementations over some other tools.

References

  1. Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software . Addison Wesley. pp.  293ff. ISBN   0-201-63361-2.
  2. "Observer Design Pattern". www.geeksforgeeks.org.
  3. Comparison between different observer pattern implementations Moshe Bindler, 2015 (Github)
  4. Differences between pub/sub and observer pattern The Observer Pattern by Adi Osmani (Safari books online)
  5. The Windows Programming Experience Charles Petzold, Nov 10, 1992, PC Magazine (Google Books)
  6. "The Observer design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  7. "jQuery - Listening for variable changes in JavaScript".
  8. "Jquery - Listening for variable changes in JavaScript".