Verständnisfrage Observer-Pattern

Status
Nicht offen für weitere Antworten.

The_S

Top Contributor
Tach,

ich hab da ein Verständnisproblem mit dem Observer-Pattern. Und zwar soll dieses Pattern ja dazu dienen, dass Klassen miteinander kommunizieren können ohne sich zu kennen und ohne Referenzen mitübergeben zu müssen und so gegen das MVC Pattern verstoßen würden (also wenn sie Referenzen übergeben würden)!?

Die implementierung und alles ist kein Problem, nur wie schaffe ich das jetzt, dass ich damit in beide Richtungen kommunizieren kann? Also z. B. Daten benachrichtigen View dass was geändert wurde. Anzeige in View wird angepasst und gut is. Aber was mache ich jetzt wenn z. B. auf der View der OK Button gedrückt wurde und deshalb Daten verändert werden müssen? Die GUI Klasse erbt ja nicht von Observable und kennt weder Controller noch Daten Klasse.

Hoffe mein Problem ist einigermaßen verständlich, hab vermutlich nur ne Denkblockade im Hirn bzw. hab das Observer Pattern noch nicht 100%ig verstanden 😳 .

Danke für die Antworten!
 
In MVC - MVC ist übrigens kein DesignPattern - handelt üblicherweiße ein Controller die Events, und der kennt sowohl Model als auch View...
 
Ok, danke. Auch wieder was gelernt 😀 .

Also dann erbt praktisch der Controller von Observable und die Daten so wie die GUI implementiert Observer!? Gut hab ich mir schon fast gedacht, nur hat mich die Beschreibung in den FAQ etwas verwirrt, da dort die Daten-Klasse von Observable erbt und nicht der Controller ???:L .

btw: Dieser Post ist noch ne Frage ob ichs richtig verstanden hab, also bitte um Antwort (nicht das mir einer denkt ich möchte es nur nochma für mich zusammenfassen 😉 ). Danke
 
Hi,
ich denke du hast das Observer-Pattern noch nicht so richtig verstanden. Das eigentlich Ziel dieses Pattern ist es keineswegs eine bestimmte Trennung vorzunehmen. Diese findet zwar indirekt statt, aber vielmehr geht es um die Konsistenzerhaltung mehrerer Beobachter.
Die eigentliche Idee ist recht einfach. Ein Objekt dass Beobachtet werden kann bietet eine Methode zum Registrieren/Deregistrieren von einem Beobachter. Ein Beobachter muss wiederum eine bestimmte Methode implementieren, über die die Benachrichtigung statt findet. Tritt ein Ereingis in dem zu Beobachtendem Objekt auf, so werden alle registrierten Beobachter benachrichtigt. Eine Beispiel implementierung wären die Listener der grafischen Komponenten. Über die Parameter der Methode, die alle Listener über das eintreten des Events informieren, werden somit alle Beobachter benachrichtigt.
In anderen Sprachen (C/C++, Delphi, ...) wurden früher gerne Funktionszeiger verwendet. Hier würde einfach der Zeiger auf eine Funktion gesetzt werden (nicht auf die Instanz eines Interface / einer Klasse). Diese Funktion wurde direkt aufgerufen, wenn ein Ereignis aufgerufen wurde. Das Problem ist nun, Listener1 setzt diesen Zeiger auf sein Funktion, dann möchte Listener2 auch informiert werden und macht das gleiche. Da es sich immer um den selben Zeiger handelt, hätte man ein Problem, es würde nicht mehr jeder etwas von einer Änderung mitbekommen.

Was das bidirektionale bei dir angeht, so sollte es kein Problem sein, dass du einfach mehr als einer Klasse eine addListener/removeListener Methode hinzufügst. Wie sie ihre Listener letztlich über das eintreten von Ereignissen informieren kann dabei natürlich beliebig (aber auch gleich) sein. Ich sehe da nicht ganz dein Problem.

Gruß Der Unwissende
 
Hm, ich glaube schon dass ich es prinzipiell verstehe, nur nicht wie ich es anwende 🙁 . Wäre supi wenn mir jemand ein kleines Beispiel geben könnte, in welchem eine Daten-Klasse über einen Controller mit der GUI "kommuniziert" und umgekehrt. 🙂
 
Hab heut meinen sozialen Tag 😀
Code:
public class View extends JButton /* nur ein Beispiel */ implements MyListener
{
	//Model hat sich geändert
	public void update(BlaBlaEvent event)
	{
			//doSomethingCool()
	}
}

class Controller
{
	private View view;
	private Model model;
	public Controller()
	{
		model.addMyListener(view);
		view.addActionListener(new ActionListener() {
		
			public void actionPerformed(ActionEvent e)
			{
				model.setRandomValue();
		
			}
		
		});
	}
	
}

class Model
{
	private List<WeakReference<MyListener>> listenerList;
	
	void addMyListener(MyListener listener)
	{
		listenerList.add(new WeakReference<MyListener>(listener));
	}
	
	void setRandomValue()
	{
		fireUpdate();
	}
	
	void fireUpdate()
	{
		Iterator<WeakReference<MyListener>> it = listenerList.iterator();
		while(it.hasNext())
			it.next().get().update(new BlaBlaEvent());
	}
}


interface MyListener
{
	public void update(BlaBlaEvent event);
}
 
Ich hab mal eine kleine Test-Applikation geschrieben. Kann ja sich ja mal wer bei Gelegenheit anschauen, ob ich das so richtig umgesetzt habe.

SoundController, von hier wird alles gesteuert

Code:
package controller;

import interfaces.ModelChangedListener;
import interfaces.ViewChangedListener;
import view.subclasses.SoundFrame;
import data.subclasses.StoreData;
import events.ModelChangedEvent;
import events.ViewChangedEvent;

public class SoundController implements ModelChangedListener, ViewChangedListener {

	private SoundFrame sf = null;
	private StoreData sd = null;
	
	public void loadApplication() {
		
		sf = new SoundFrame();
		sd = new StoreData();
		
		sf.addViewChangedListener(this);
		sd.addModelChangedListener(this);
		
		sf.fireTestString("Hallo Model, ich bin die View");
		sd.fireTestString("Hallo View, ich bin das Model");
	}
	
	public void notifyView(ModelChangedEvent mce) {
		
		sf.modelChanged(mce.getNote());
	}
	
	public void notifyModel(ViewChangedEvent vce) {
		
		sd.viewChanged(vce.getNote());
	}
}

Hier der Source für die beiden Listener ViewChangedListener und ModelChangedListener


Code:
package interfaces;

import events.ModelChangedEvent;

public interface ModelChangedListener {

	public void notifyView(ModelChangedEvent mcd);
}

Code:
package interfaces;

import events.ViewChangedEvent;

public interface ViewChangedListener {

	public void notifyModel(ViewChangedEvent vce);
}

Die beiden Events ViewChangedEvent und ModelChagnedEvent

Code:
package events;

public class ModelChangedEvent {

	private Object note = null;
	private Object source = null;
	
	public ModelChangedEvent(Object note, Object source) {
		
		setNote(note);
		setSource(source);
	}

	public Object getNote() {
		return note;
	}
	
	public void setNote(Object note) {
		this.note = note;
	}

	public Object getSource() {
		return source;
	}

	public void setSource(Object source) {
		this.source = source;
	}
}

Code:
package events;

public class ViewChangedEvent {

	private Object note = null;
	private Object source = null;
	
	public ViewChangedEvent(Object note, Object source) {
		
		setNote(note);
		setSource(source);
	}

	public Object getNote() {
		return note;
	}

	public void setNote(Object note) {
		this.note = note;
	}

	public Object getSource() {
		return source;
	}

	public void setSource(Object source) {
		this.source = source;
	}
}

Die Superklasse für StoreData

Code:
package data.superclasses;

import interfaces.ModelChangedListener;
import java.util.Vector;
import events.ModelChangedEvent;


public abstract class Data { 

	private static final long serialVersionUID = 1L;
	private Vector<ModelChangedListener> listener = null;
	
	public Data() {
		
		listener = new Vector<ModelChangedListener>();
	}
	
	public void addModelChangedListener(ModelChangedListener mcl) {
		
		listener.add(mcl);
	}
	
	public void removeModelChangedListener(ModelChangedListener mcl) {
		
		listener.remove(mcl);
	}
	
	protected void fireUpdate(ModelChangedEvent mce) {
		
		for (ModelChangedListener mcl : listener) {
			mcl.notifyView(mce);
		}
	}
	
	public abstract void viewChanged(Object obj);
}

Die Super-Klasse für SoundFrame

Code:
package view.superclasses;

import interfaces.ViewChangedListener;
import java.util.Vector;
import javax.swing.JFrame;
import events.ViewChangedEvent;

public abstract class ViewJFrame extends JFrame{

	private static final long serialVersionUID = 1L;
	private Vector<ViewChangedListener> listener = null;
	
	public ViewJFrame() {
		
		listener = new Vector<ViewChangedListener>();
	}
	
	public void addViewChangedListener(ViewChangedListener vcl) {
		
		listener.add(vcl);
	}
	
	public void removeViewChangedListener(ViewChangedListener vcl) {
		
		listener.remove(vcl);
	}
	
	protected void fireUpdate(ViewChangedEvent vce) {
		
		for (ViewChangedListener vcl : listener) {
			vcl.notifyModel(vce);
		}
	}
	
	public abstract void modelChanged(Object obj);
}

SoundFrame

Code:
package view.subclasses;

import view.superclasses.ViewJFrame;
import events.ViewChangedEvent;

public class SoundFrame extends ViewJFrame {

	private final static long serialVersionUID = 1L;
	
	public void fireTestString(String str) {
		
		fireUpdate(new ViewChangedEvent(str, this));
	}
	
	@Override
	public void modelChanged(Object obj) {
		
		System.out.println("SoundFrame hat folgende Nachricht erhalten: " + obj);
	}

}

StoreData

Code:
package data.subclasses;

import data.superclasses.Data;
import events.ModelChangedEvent;

public class StoreData extends Data {

	public void fireTestString(String str) {
		
		fireUpdate(new ModelChangedEvent(str, this));
	}

	@Override
	public void viewChanged(Object obj) {
		
		System.out.println("StoreData hat folgende Nachricht erhalten: " + obj);
	}

}

Danke fürs durchlesen 😀
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben