Aufrufende Klasse ermitteln

Hallo,

ich weiß ja nicht wie du es versucht hast, aber ich denke mal, dass du das Objekt callerClass auf dein Model casten wolltest.

So würde es funktionieren. Natürlich vorrausgesetzt, dass es auch von dieser Klasse ist.
Java:
Object o = callerClass.newInstance();
((dein Model)o).deineMethode;

MFG

zEriX
 
Hey, dann kann man doch gleich mit new eine Instanz erzeugen und ich frage mich, was das dann soll. Hier fliegen doch die Begriffe Instanz und Klasse durcheinander. Ich habe das so verstanden, dass normaler_spinner eine Methode in einer konkreten Instanz aufrufen will. Das geht aber auch mit Thomas Lösung nicht. Seine Lösung sagt Dir nur den Typ, aber nicht das Objekt. Oder sehe ich das hier komplett falsch.

Grüße
 
Hallo,

Das geht aber auch mit Thomas Lösung nicht. Seine Lösung sagt Dir nur den Typ, aber nicht das Objekt. Oder sehe ich das hier komplett falsch.
Das siehst du richtig... ich sagte in meinem Posting ja, das hier etwas durcheinander geworfen wird... ich hab dann trotzdem mal ein zum Thread-Titel passendes Beispiel gemacht ;-)

Gruß Tom
 
Ja, zu meiner auch, dachte das wärs gewesen. Anscheinend ist es nicht ganz trivial. Dafür hab ich aber ersten Kontakt mit der Reflection gehabt, wer weiß wann mans gebrauchen kann :)
 
Hi,

ich muss noch mal darauf rum reiten. Vielleicht solltest Du Dein Design noch mal überdenken. Das mit dem Observerpattern von Laocoon ist sicher ein zielführender Vorschlag. Mein Tipp:

Du hast ganz unten eine Model-Klasse, die nix, aber auch gar nix von Views oder anderen Klassen weiss. Dafür erbst sie von Observable, bzw implementiert einen eigenen Benachrichtigungsmechanismus. Jeder Aufruf eines Setters in der Modelklasse löst ein Updateereignisaus.

Du machst eine zweite Klasse, den Controller (Adapter). Diese Klasse registriert sich sowohl beim Model, als auch beim View als Listener. (implementiert Observer, JListModel, oder was auch immer). Bei der Konstruktion der Klasse, gibst Du Referenzen, sowohl auf den View, als auch auf das Model mit und registrierst die Klasse bei beiden als Listener.


Wenn jetzt im View ein Ereignis feuert, wird der Controller benachrichtigt. Er hat aber eine Referenz auf das Model und kann den (die) geigneten Setter aufrufen. Umgekehrt wird jeder Setteraufruf im Model mit einem Updateereignis feuern. Der Contoller bekommt das mit und zeichnet den View neu. Voila.

Falls ich heute noch ein wenig Zeit habe, stelle ich eine simple Lösung ein.

Gruß

Edit:

Hier eine Lösung mit JFace

Zum Laufen lassen müsst ihr folgende Jars aus Eclipse einbinden

Die beiden org.eclipse.swt jars, org.eclipse.jface und org.eclipse.jface.text, sowie org.eclipse.core.command

Dann als SWT-Application starten.

Die Klasse ContentProvider ist der Controller, der die Zeilen aus dem Model liefert. In Swing würde er das ListModel implementieren. Statt des Listviewer könnte man dann genausogut eine JList nehmen. Er registriert sich beim Model als Listener und zeichnet den Viewer neu sobald sich irgendwas im Model ändert. Gleichzeitig registriert er sich beim viewer als doubleClickListener und läßt das doppeltgeklickte Schwein fressen.

Der Viewer hat keine Ahnung von der Struktur des Models und das Model keine Ahnung von der Struktur (oder gar der Existens) des Views.

Der Button "neues Schwein" soll nur zeigen, dass das Schreiben ins Model sofort ein Neuzeichnen des Views auslöst.

Die Tiere ;-)

Java:
package de.tutorials.tiere;

import java.util.Observable;

public class Schwein extends Observable{
	
	private String name;
	private int gewicht;
	
	public Schwein() {
		this("nobody");
	}
	
	public Schwein(String name) {
		setName(name);
		setGewicht(10);
	}

	public int getGewicht() {
		return gewicht;
	}

	private void setGewicht(int gewicht) {
		this.gewicht = gewicht;
		fireEvent();
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
		fireEvent();
	}
	
	public void fressen() {
		setGewicht(getGewicht() + 1);
	}

	@Override
	public String toString() {
		
		return "Schwein [Name="+getName()+", Gewicht=" + getGewicht() + "]";
	}
	
	private void fireEvent() {
		setChanged();
		notifyObservers();
	}
	
	
	

}

Der Container für die Tiere (model)

Java:
package de.tutorials.tiere;

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

public class SchweineModel extends Observable implements Observer{

	private List<Schwein> schweine = new ArrayList<Schwein>();
	
	public void add(Schwein e) {
		e.addObserver(this);
		schweine.add(e);
		fireEvent();
	}

	public void clear() {
		for(Schwein schwein: schweine)
			schwein.deleteObserver(this);
		schweine.clear();
		fireEvent();
	}

	public void remove(Schwein o) {
		o.deleteObserver(this);
		schweine.remove(o);
		fireEvent();
	}

	public Object[] toArray() {
		return schweine.toArray();
	}

	public void update(Observable o, Object arg) {
		fireEvent();
		
	}
 
	private void fireEvent() {
		setChanged();
		notifyObservers();
	}
	

}

Der Controller (Adapter, Zwischenstück, wie auch immer)

Java:
package de.tutorials.tiere.provider;

import java.util.Observable;
import java.util.Observer;

import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.Viewer;

import de.tutorials.tiere.Schwein;
import de.tutorials.tiere.SchweineModel;

public class SchweineContentProvider implements IStructuredContentProvider, Observer, IDoubleClickListener {

	private ListViewer viewer;

	public Object[] getElements(Object inputElement) {
		SchweineModel model = (SchweineModel) inputElement;
		return model.toArray();
	}

	public void dispose() {
		

	}

	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		this.viewer = (ListViewer) viewer;
		this.viewer.removeDoubleClickListener(this);
		this.viewer.addDoubleClickListener(this);
		if (oldInput == null)
			((SchweineModel) newInput).addObserver(this); 
	}

	public void update(Observable o, Object arg) {
		//plumpes Neuzeichnen durch neusetzten der Quelle. (geht auch besser ;-))
		SchweineModel model = (SchweineModel) o;
		viewer.setInput(model);
	}

	public void doubleClick(DoubleClickEvent event) {
		IStructuredSelection selection = (IStructuredSelection) event.getSelection();
		Schwein schwein = (Schwein) selection.getFirstElement();
		schwein.fressen();
		
	}

}

Der Labelprovider liefert nur den String zum Object aus der Zeile, wäre in Swing Teil des ListModels
Java:
package de.tutorials.tiere.provider;

import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;

public class SchweineModelLabelProvider extends LabelProvider {

	@Override
	public Image getImage(Object element) {
		
		return null;
	}

	@Override
	public String getText(Object element) {
		
		return element.toString();
	}

}

Und zuletzt der View

Java:
package de.tutorials.gui;



import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import de.tutorials.tiere.Schwein;
import de.tutorials.tiere.SchweineModel;
import de.tutorials.tiere.provider.SchweineContentProvider;
import de.tutorials.tiere.provider.SchweineModelLabelProvider;

public class SchweineViewer {
	
	private static final Display display = new Display();
	private Shell shell = null;
	private ListViewer viewer = null;
	private SchweineModel model = null;
	private Button button = null;
	
	
	SchweineViewer() {
		getViewer();
		getButton();
		dispatch();
	}

	
	private void dispatch() {
		
		getShell().open();
		while(! getShell().isDisposed()) {
			if(! getDisplay().readAndDispatch()) 
				getDisplay().sleep();
		}
	}

	

	private static Display getDisplay() {
		return display;
	}


	private Shell getShell() {
		if(shell == null){
			shell = new Shell(getDisplay());
			shell.setLayout(new FillLayout());
		}
		return shell;
	}


	private ListViewer getViewer() {
		if(viewer == null){
			viewer = new ListViewer(getShell(), SWT.SINGLE);
			viewer.setContentProvider(new SchweineContentProvider());
			viewer.setLabelProvider(new SchweineModelLabelProvider());
			viewer.setInput(getModel());
		}
		return viewer;
	}


	private SchweineModel getModel() {
		if(model == null) {
			model = new SchweineModel();
			model.add(new Schwein("Piggy"));
			model.add(new Schwein("Babe"));
		}
		return model;
	}
	
	


	private Button getButton() {
		if(button == null) {
			button = new Button(getShell(), SWT.PUSH);
			button.setText("Neues Schwein!");
			button.addSelectionListener(new SelectionAdapter(){

				@Override
				public void widgetSelected(SelectionEvent e) {
					getModel().add(new Schwein());
				}}) ;
		}
		return button;
	}


	public static void main(String[] args) {
		new SchweineViewer();

	}

}
 
Zuletzt bearbeitet:
Wenn die View keine Ahnung hat von dem Model, warum existiert dann in der View eine Instanz von Model?
Das ist ja das was ich jetzt strukturtechnisch nicht ganz auf die Reihe bekomme:
Ich habe einen Controller, der kennt View und Model. In der View sollen die Klassen aus dem Model gezeichnet werden. Ändert sich das Model, wird eine Ereignis an die View gegeben. Und die muss sich ja jetzt irgendwo her die Klassen holen die im Model existieren. Wenn jetzt die View das Model aber nicht kennt, da strikte Trennung, und den eigenen Controller ja auch nicht, kann das ja irgendwie nicht funktionieren. Also müßte das Model ein Ereignis an den Controller schicken, der wiederum holt sich die Elemente aus dem Model und gibt sie an die View weiter.
Das scheint mir dann aber doch ein langer langer Weg zu sein, gerade weil mein Model alle 200ms mit Daten aus einer SPS gefüttert wird und die Daten in annähernd Echtzeit auf die View sollen. Um mir den Weg über den Controller zu sparen, kennt das Model die View aber nicht umgekehrt, daher mal die ursprüngliche Fragestellung des Thread.

Wie bekomme ich jetzt eine saubere Lösung die sowohl dem MVC entspricht aber trotzdem schnell genug ist?
 
Wenn es sich immer um die gleichen Daten handelt geht das sogar mit dem Observer Pattern.
Dein Modell ruft ja jedesmal im View die update Methode auf. Der kannst du doch einfach die geänderten Daten als Parameter mitgeben. Damit muss die View dann nicht wieder selbst aktiv werden und sich die Daten vom Modell holen.

Grüße
Daniel
 
Wenn die View keine Ahnung hat von dem Model, warum existiert dann in der View eine Instanz von Model?

Die View gibt die Referenz nur an den ContentProvider weiter. Man könnte auch eine Lösung basteln, bei der der View nichts von dem Model mitgekommt. Aber bei JFace ist das halt so.

Ich habe einen Controller, der kennt View und Model. In der View sollen die Klassen aus dem Model gezeichnet werden. Ändert sich das Model, wird eine Ereignis an die View gegeben. Und die muss sich ja jetzt irgendwo her die Klassen holen die im Model existieren. Wenn jetzt die View das Model aber nicht kennt, da strikte Trennung, und den eigenen Controller ja auch nicht, kann das ja irgendwie nicht funktionieren.

Der Contoller ist technisch ein Adapter. Er kennt die Schnittstellen des Models und die des Views. Deswegen kann der Listviewer ja alles mögliche anzeigen ohne verändert werden zu müssen. Dem Model ist egal wer darauf schaut. Es wäre auch nicht schlimm, wenn der View Kenntnis vom Model hat. In vielen Fällen läßt sich das kaum vermeiden. Umgekehrt wäre es schlimmer. Ich müßte ja bei jedem neuen View, das Model anpassen, was zu sehr unruhigem und schlecht wiederverwendbaren Code führt. Ein Adapter verbindet die Dinge, ohne selbst Logik zu haben. Wenn Du nach Spanien in den Urlaub fährst und Deinen Föhn mitnimmst, kommst Du ja auch nicht auf die Idee, Werkzeug mitzunehmen um die Dose (bzw Föhn) vor Ort passend zu machen.


Also müßte das Model ein Ereignis an den Controller schicken, der wiederum holt sich die Elemente aus dem Model und gibt sie an die View weiter.

Jein. Ich bevorzuge diese Lösung, weil sie elegant ist. Es gibt aber auch Fälle, da sagt der Controller dem View nur, dass er sich ganz oder teilweise neu zeichen muss, voraufhin der View die Daten direkt vom Model holt. Microsoft packt den Controller oft ins Model und nennt das dann Dokument. Geht auch. Aber nehmen wir folgende Situation. Du hast sagen wir das Dateisystem als Model und willst einen File-Explorer bauen. Der besteht aus zwei Views. Einem TreeView und einem ListView auf die gleichen Daten. Ein TreeView hat eine ganz andere Schnittstelle als ein ListView. Der einfachste Weg, das zu Lösen ist zwei Controller zu schreiben. Einer verbindet den Tree mit dem Dateisystem, der andere die Liste. Egal ob Tree oder Liste (sagen wir durch ein Rename, oder Delete) den Datenbestand ändern, wird zunächst ein Setter im Model aufgerufen, der feuert mit einem Ereignis, die Controller lauschen und sagen ihren Views zeichnet euch neu. Jetzt kommt ein TableView hinzu. Die anderen Controller sowie, das Model musst Du nicht mehr anfassen. Du schreibst einen einfachen Controller, und schon wird zusätzlich die Tabelle versorgt. Wenn wir uns das weiter vor Augen führen, wirst Du sehen, das bei Swing dummerweise TableModel und ListModel nicht gleich sind. Hättest Du keine getrennten oder gar keine Controller, müsste die ein und selbe Klasse lauter sehr ähnliche Methoden implementieren um sowohl Liste als auch Tabelle zu versorgen. Der Code wäre nicht nur unschön, er müsste auch bei jeder Änderung der Views wieder geöffnet werden.

Das scheint mir dann aber doch ein langer langer Weg zu sein, gerade weil mein Model alle 200ms mit Daten aus einer SPS gefüttert wird und die Daten in annähernd Echtzeit auf die View sollen. Um mir den Weg über den Controller zu sparen, kennt das Model die View aber nicht umgekehrt, daher mal die ursprüngliche Fragestellung des Thread.

Der Weg ist gar nicht so lang. Es ist nur ein kleiner weiterer UP-Aufruf. Davon bekommst Du in 200 millis eine Menge unter ;-). Was wäre die Alternative. Entweder passt Du das Model extrem an den View an, dann hast Du aber kein MVC-Pattern mehr, sondern eine harte Codierung. Du hast nicht mehr 2 wiederverwendbare Module, sondern nur noch eine enge Kopplung, nur einen monolitischen Block. Die beiden Klassen können nur noch zusammen existieren, also kannst Du gleich eine daraus machen. Und bei jeder Änderung musst Du beide anfassen (brrr gruselig). Oder aber du machts die Anpassung sagen innerhalb des Views und dann hast Du dort auch wieder Code. Du gewinnst also nichts.

Natürlich sind Pattern nicht immer für optimale Performance bekannt ;-). das ist auch nicht (immer) ihr Ziel. In der Regel muss man sich für optimale Performance oder optimale Wartbarkeit entscheiden. In diesem speziellen Fall, wird die Performance sicher nicht leiden. Um bei dem Beispiel des Föhns zu bleiben; Natürlich kostet ein Adapter Leistung. An der Dose rumzuschrauben spart vielleicht ein paar microwatt. Ich wette aber, Deine Haare werden genauso schnell trocken.
 
Zurück