Observer Ontwerppatroon in Java

Observerpatroon is een van de gedragsontwerppatronen. Het observer-ontwerppatroon is nuttig wanneer je geïnteresseerd bent in de status van een object en op de hoogte wilt worden gebracht wanneer er een verandering optreedt. In het observerpatroon worden de objecten die de status van een ander object observeren Observer genoemd, en het object dat wordt geobserveerd wordt Onderwerp genoemd.

Observer Ontwerppatroon

Volgens GoF is het doel van het observer-ontwerppatroon;

Definieer een één-op-veel afhankelijkheid tussen objecten, zodat wanneer één object van status verandert, al zijn afhankelijken automatisch worden geïnformeerd en bijgewerkt.

Onderwerp bevat een lijst van waarnemers om op de hoogte te worden gesteld van elke verandering in zijn toestand, dus het moet methoden bieden waarmee waarnemers zichzelf kunnen registreren en afmelden. Het onderwerp bevat ook een methode om alle waarnemers op de hoogte te stellen van elke verandering, en het kan de update verzenden terwijl het de waarnemer op de hoogte stelt, of het kan een andere methode bieden om de update te krijgen. Een waarnemer moet een methode hebben om het object in de gaten te houden en een andere methode die door het onderwerp wordt gebruikt om hen op de hoogte te stellen van eventuele updates. Java biedt een ingebouwd platform voor het implementeren van het Observer-patroon via de java.util.Observable-klasse en de java.util.Observer-interface. Het wordt echter niet veel gebruikt omdat de implementatie erg eenvoudig is en we meestal niet willen eindigen met het uitbreiden van een klasse alleen maar voor het implementeren van het Observer-patroon, aangezien Java geen meervoudige overerving in klassen biedt. Java Message Service (JMS) gebruikt het Observer-ontwerppatroon samen met het Bemiddelingspatroon om applicaties in staat te stellen zich te abonneren en gegevens te publiceren naar andere applicaties. Model-View-Controller (MVC)-frameworks gebruiken ook het Observer-patroon waarbij Model het Onderwerp is en Weergaven waarnemers zijn die zich kunnen registreren om op de hoogte te worden gesteld van elke wijziging in het model.

Voorbeeld van Observer-patroon in Java

Voor ons Java-programma voor het observer-patroon zouden we een eenvoudig onderwerp implementeren, en waarnemers kunnen zich registreren voor dit onderwerp. Telkens wanneer er een nieuw bericht naar het onderwerp wordt gepost, worden alle geregistreerde waarnemers op de hoogte gesteld en kunnen ze het bericht consumeren. Op basis van de eisen van Subject, hier is de basis Subject-interface die de contractmethoden definieert die moeten worden geïmplementeerd door elk concreet subject.

package com.journaldev.design.observer;

public interface Subject {

	//methoden om waarnemers te registreren en deregistreren
	public void register(Observer obj);
	public void unregister(Observer obj);
	
	//methode om waarnemers op de hoogte te stellen van wijzigingen
	public void notifyObservers();
	
	//methode om updates van het subject te verkrijgen
	public Object getUpdate(Observer obj);
	
}

Vervolgens zullen we een contract creëren voor Observer, waarin een methode zal zijn om het Subject aan de waarnemer te koppelen en een andere methode die door het Subject wordt gebruikt om op de hoogte te worden gesteld van eventuele wijzigingen.

package com.journaldev.design.observer;

public interface Observer {
	
	//methode om de waarnemer bij te werken, gebruikt door het subject
	public void update();
	
	//koppelen aan het subject om te observeren
	public void setSubject(Subject sub);
}

Nu is ons contract klaar, laten we verder gaan met de concrete implementatie van ons onderwerp.

package com.journaldev.design.observer;

import java.util.ArrayList;
import java.util.List;

public class MyTopic implements Subject {

	private List observers;
	private String message;
	private boolean changed;
	private final Object MUTEX= new Object();
	
	public MyTopic(){
		this.observers=new ArrayList<>();
	}
	@Override
	public void register(Observer obj) {
		if(obj == null) throw new NullPointerException("Null Observer");
		synchronized (MUTEX) {
		if(!observers.contains(obj)) observers.add(obj);
		}
	}

	@Override
	public void unregister(Observer obj) {
		synchronized (MUTEX) {
		observers.remove(obj);
		}
	}

	@Override
	public void notifyObservers() {
		List observersLocal = null;
		//synchronisatie wordt gebruikt om ervoor te zorgen dat een waarnemer die zich heeft geregistreerd nadat het bericht is ontvangen, niet op de hoogte wordt gesteld
		synchronized (MUTEX) {
			if (!changed)
				return;
			observersLocal = new ArrayList<>(this.observers);
			this.changed=false;
		}
		for (Observer obj : observersLocal) {
			obj.update();
		}

	}

	@Override
	public Object getUpdate(Observer obj) {
		return this.message;
	}
	
	//methode om een bericht naar het onderwerp te sturen
	public void postMessage(String msg){
		System.out.println("Message Posted to Topic:"+msg);
		this.message=msg;
		this.changed=true;
		notifyObservers();
	}

}

De implementatie van de methode om een waarnemer te registreren en te deregistreren is heel eenvoudig, de extra methode is postMessage() die door de clienttoepassing zal worden gebruikt om een String-bericht naar het onderwerp te sturen. Let op de boolean variabele om de verandering in de toestand van het onderwerp bij te houden en die wordt gebruikt bij het informeren van de waarnemers. Deze variabele is vereist zodat als er geen update is en iemand de methode notifyObservers() aanroept, er geen valse meldingen naar de waarnemers worden gestuurd. Let ook op het gebruik van synchronisatie in de methode notifyObservers() om ervoor te zorgen dat de melding alleen wordt verzonden naar de waarnemers die zijn geregistreerd voordat het bericht naar het onderwerp wordt gepubliceerd. Hier is de implementatie van Waarnemers die toezicht houden op het onderwerp.

package com.journaldev.design.observer;

public class MyTopicSubscriber implements Observer {
	
	private String name;
	private Subject topic;
	
	public MyTopicSubscriber(String nm){
		this.name=nm;
	}
	@Override
	public void update() {
		String msg = (String) topic.getUpdate(this);
		if(msg == null){
			System.out.println(name+":: No new message");
		}else
		System.out.println(name+":: Consuming message::"+msg);
	}

	@Override
	public void setSubject(Subject sub) {
		this.topic=sub;
	}

}

Let op de implementatie van de methode update() waarbij de methode getUpdate() van het Onderwerp wordt aangeroepen om het bericht te consumeren. We hadden deze oproep kunnen vermijden door het bericht als argument door te geven aan de methode update(). Hier is een eenvoudig testprogramma om onze onderwerpsimplementatie te consumeren.

package com.journaldev.design.observer;

public class ObserverPatternTest {

	public static void main(String[] args) {
		//maak onderwerp
		MyTopic topic = new MyTopic();
		
		//maak waarnemers
		Observer obj1 = new MyTopicSubscriber("Obj1");
		Observer obj2 = new MyTopicSubscriber("Obj2");
		Observer obj3 = new MyTopicSubscriber("Obj3");
		
		//registreer waarnemers bij het onderwerp
		topic.register(obj1);
		topic.register(obj2);
		topic.register(obj3);
		
		//voeg waarnemer toe aan onderwerp
		obj1.setSubject(topic);
		obj2.setSubject(topic);
		obj3.setSubject(topic);
		
		//controleer of er updates beschikbaar zijn
		obj1.update();
		
		//stuur nu bericht naar onderwerp
		topic.postMessage("New Message");
	}

}

Als we het bovenstaande programma uitvoeren, krijgen we de volgende output.

Obj1:: No new message
Message Posted to Topic:New Message
Obj1:: Consuming message::New Message
Obj2:: Consuming message::New Message
Obj3:: Consuming message::New Message

Java Observer Patroon Klasse Diagram

Het Observer-ontwerppatroon wordt ook wel het publiceer-abonneepatroon genoemd. Enkele van de implementaties ervan zijn;

  • java.util.EventListener in Swing
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener

Dat is alles voor het Observer-ontwerppatroon in Java, ik hoop dat je het leuk vond. Deel je liefde met opmerkingen en door het te delen met anderen.

Source:
https://www.digitalocean.com/community/tutorials/observer-design-pattern-in-java