Observer (ontwerppatroon)
Observer (ook bekend als Subject-Observer, Dependents of Publish-Subscribe) is een ontwerppatroon in de object-georiënteerde softwareontwikkeling. Het patroon beschrijft een efficiënte en redelijk ontkoppelde manier waarop objecten in een programma kennis kunnen nemen van relevante toestandsveranderingen binnen andere objecten in hetzelfde programma.
Toepassing
bewerkenHet observerpatroon is van toepassing op situaties waarin een entiteit binnen een programma moet reageren op een gebeurtenis binnen hetzelfde programma, maar waarbij de toestandsverandering niet in die entiteit zelf plaatsvindt. Het klassieke voorbeeld hiervan is een spreadsheet: als de waarde in een cel verandert, moeten bijvoorbeeld grafieken die die waarde weergeven mee veranderen. Maar er zijn legio andere voorbeelden te bedenken: het opvragen van een pagina in een webapplicatie veroorzaakt het aanpassen van statistieken over het gebruik van de applicatie, het invoegen van een rij in een tabel in een database zorgt ervoor dat een "trigger" afgevuurd wordt, een beweging van de muis zorgt ervoor dat de cursor op het scherm verplaatst wordt, een hartbewakingsmonitor die opmerkt dat het hartritme buiten de gestelde waarden treedt, doet een alarm afgaan bij de verpleging.
Het doel in al deze gevallen is dat een toestandsverandering in een object opgemerkt wordt in een ander object. En dit het liefst zonder dat de objecten sterk gekoppeld zijn (zodat de objecten vrij onafhankelijk van elkaar bestaan en wellicht hergebruikt kunnen worden) en zonder dat het opmerken al te duur is (in het geval van een spreadsheet: om een grafiek actueel te houden, is het bij voorkeur niet nodig dat iedere microseconde de hele spreadsheet doorlopen wordt om veranderingen te detecteren).
Structuur
bewerkenHet observerpatroon voldoet aan deze doelstellingen door het instellen van de rollen Subject en Observer binnen het programma. Een Subject is een object waarvan de toestand gevolgd wordt door een Observer.
Het patroon realiseert minimale koppeling tussen objecten door de rolverdeling om te keren: een Observer registreert de toestandsverandering van het Subject niet; het Subject publiceert toestandsveranderingen aan geïnteresseerde Observers. Een Subject beschikt daartoe over een notify() methode die aan alle geïnteresseerde Observers het update()-bericht stuurt. Dit heeft als voordeel dat de Observers alleen een interface hoeven te hebben om veranderingen gepubliceerd te krijgen en verder niets over een Subject hoeven te weten - geen naam, geen informatie over de interne toestand, niets. Een Observer wordt zo een generiek iets dat alleen zijn eigen toestand moet kennen om een update uit te voeren, maar verder wellicht zelfs op verschillende typen Subject kan reageren.
Naast notify() beschikt Subject ook over methoden om geïnteresseerde Observers te verbinden aan het Subject of er weer van los te maken (attach() en detach()). Een geïnteresseerde Observer is verbonden en wordt door het Subject op de hoogte gesteld van veranderingen. Merk op dat ook Subject niet sterk gekoppeld is aan een ander object; een Subject hoeft alleen de naam (en het type) Observer te kennen.
Uiteraard is het wel nodig dat op een bepaald moment ergens in het programma zowel Subject als Observer bekend zijn: als een Observer verbonden wordt aan een Subject of ervan losgemaakt. Dit kan bijvoorbeeld via een Factory geregeld worden.
Implementatievraagstukken
bewerkenEr is een aantal zaken dat met de nodige aandacht behandeld moet worden bij het gebruik van dit patroon:
- Er moet goed gelet worden op de mogelijkheid van onverwachte updates, zeker als de Observer een toestandsverandering kan veroorzaken in een Subject. Als een Subject geobserveerd wordt door meerdere Observers die niet van elkaar afweten, kan een ondoordachte toestandsverandering in een Subject leiden tot een oneindige keten van verandering, notificatie, verandering in Subject door Observer, notificatie, verandering in Subject door andere Observer, notificatie, verandering in Subject door eerste Observer, etc.
- Als een Observer meer informatie nodig heeft dan alleen dat er iets gebeurd is in het Subject (bijvoorbeeld: wat er precies gebeurd is), dan moet goed nagedacht worden over hoe dit geregeld dient te worden. Men kan ontkoppeling laten vallen en de Observer de mogelijkheid geven in het Subject te kijken. Men kan ook het update()-bericht uitbreiden met een parameter die de verandering beschrijft. De keuze hiertussen hangt af van de noden van het systeem met betrekking tot reactietijd, geheugenruimte, onderhoudbaarheid, enzovoorts.
- Als de Observer in het Subject kan kijken, moet de ontwikkelaar goed oppassen geen update()-berichten te versturen voor de toestandsverandering binnen het Subject volledig verwerkt is. Anders ziet iedere Observer een halve toestand.
- Van tijd tot tijd is het wenselijk de implementatie ingewikkelder te maken. Door notify() (of de aanroeper van notify()) intelligenter te maken, kan men bijvoorbeeld verschillende toestandsveranderingen samenpakken in een enkele update(). Of men kan extra effect bereiken door een uitgebreidere communicatie op te zetten tussen Subject en Observer.