Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
ich habe mir das Tutorial zu MVC auf MVC - Sun durchgelesen.
Dort werden die Änderungen am Model an den Controller weitergegeben. Wann ist dies sinnvoll und wann sollten diese direkt an die Views geleitet werden?
Wenn ein Model mehrere Variablen hat und sich ein Wert ändert, wie kann ich der View mitteilen, welcher Wert sich geändert hat (sodass die View nicht alle Werte des Models neu darstellen muss).
Auf der Sun-Seite wird dies über Reflection gelöst. Ist dies die einzige Möglichkeit?
notifyObservers(Object o); //der Observer bekommt dann bei update(Observable arg, Object o) o!=null
Dem Model sollte egal sein wie Controller/View aussehen, das Model sagt einmal notifyObservers und sagt damit allen die es wissen wollen das sich was ändert.
Ob dann der Controller ein Observer ist oder nicht ist dann seine Sache. (Aber der Controller sollte ja das Model ändern - wenn er dann benachrichtigt wird das sich was geändert hat, was bringt das?)
Ich kann zwar über notifyObservers(Object o); der View eine Information zukommen lassen, aber wenn das Model zum Beispiel über 5 int Werte verfügt und sich nur einer geändert hat, woher weiß die View in der Update Methode, welchen Wert sie neu darstellen muss?
(ohne sie alle 5 zu erfragen und neu anzuzeigen)
Ein Wrapper-Objekt übergeben mit dem der view eindeutig den int identifizieren kann, z.b. so:
Java:
public enum Irgendwas{
Integer1, Integer2, Integer3;
}
public View implements Observer{
public void update(Observable arg, Object o){
Irgendwas i = null;
if(o instanceof Irgendwas) i=(Irgendwas)o;
switch(i){
case Integer1: model.getInteger1(); break;
case Integer2: model.getInteger2(); break;
case Integer3: model.getInteger3(); break;
}
}
Oder noch einfacher einfach alle Werte dem Wrapper übergeben (List) und dann die Werte die drin sind übertragen.
Wobei eigentlich der View nur die getMethoden aufruft, und da würde ich persönlich nicht viel Logik implementieren (-> kostengünstiger Aufruf -> kann man auch alle 5 Aufrufen).
Hmmm... ganz allgemein: Das Observer-Pattern ist praktisch ein "Teil" des MVC-Patterns - aber sehr speziell. Insbesondere macht es bei eigenen MVC-Strukturen IMHO meistens keinen Sinn, die Observer/Observable-Klassen von Java zu verwenden, weil sie eben genau solche Dinge nicht oder nur sehr unschön abbilden können. Man könnte zwar in den "args" beim notify irgendwas unterbringen, oder ganz pragmatisch bei jeder Änderung die ganze View updaten, aber das wäre beides IMHO nicht so hübsch.
Stattdessen solltest du in Erwägung ziehen, dein eigenes Modell (Observable) und dazu passende Listener (Observer) zu erstellen, und dazu passende Events (.... ja, das ist genau das, wozu es beim einfachen Observer/Observable keine Entsprechung gibt).
Also anstatt sowas zu machen
Java:
class CustomerModel extends Observable
{
void buySomething() { /* Änderungen, dann ... */ notifyObservers(); }
void sellSomething() { /* Änderungen, dann ... */ notifyObservers(); }
}
class CustomerView implements Observer
{
private CustomerModel customerModel;
void update()
{
// Bei JEDER änderung ALLES updaten
boughtStuffList.updateWithDataFrom(customerModel);
soldStuffList.updateWithDataFrom(customerModel);
}
}
Könntest du das ganze "feingranularer" aufbauen:
Java:
interface CustomerModel
{
void buySomething();
void sellSomething();
void addCustomerListener(CustomerListener cl);
void removeCustomerListener(CustomerListener cl);
}
class DefaultCustomerModel implements CustomerModel
{
// Default-Implementierung der obigen Methoden
}
interface CustomerListener
{
void boughtSomething(CustomerEvent e);
void soldSomething(CustomerEvent e);
}
class CustomerEvent
{
public int getType() { /* liefert eine Konstante SOLD oder BOUGHT */ }
public Object getTradedItem() { /* Liefert das ge- oder verkaufte Ding */ }
}
class CustomerView implements CustomerListener
{
private CustomerModel customerModel;
public void boughtSomething(CustomerEvent e)
{
// NUR das nötige updaten:
boughtStuffList.add(e.getTradedItem());
}
public void soldSomething(CustomerEvent e)
{
// NUR das nötige updaten:
soldStuffList.add(e.getTradedItem());
}
}
Nur von der Idee her - die genaue Struktur musst du dir halt überlegen, aber wenn du dir mal die Klassen von Swing ansiehst, ist das eben so häufig so, dass man es schon fast als (Entwurfs-)Muster bezeichnen könnte :
interface TableModel
class DefaultTableModel implements TableModel
interface TableModelListener
class TableModelEvent
class JTable (die View) implements TableModelListener
interface ListModel
class DefaultListModel implements ListModel
interface ListDataListener
class ListDataEvent
class JList (die View) implements ListDataListener
Für sowas bietet sich der PropertyChangeListener
Das Model hat eine Liste aller PropertyChangeListener und feuert dann wenn sich was verändert hat die PropertyChangeEvent event, welche die View(s) dann auswerten kann/können...
Ach ist ja im Sun tutorial drin
Auszug:
Java:
public abstract class AbstractModel
{
protected PropertyChangeSupport propertyChangeSupport;
public AbstractModel()
{
propertyChangeSupport = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
}
Plakativ formuliert: Man könnte ja IMMER und wirklich für ALLES PropertyChangedEvents verwenden - warum überhaupt noch eigene Listener/Events, wenn damit alles abgebildet werden kann? Oder noch weiter getrieben: Wie wäre es, wenn man alle Listener-Interfaces und Events "deprecated", und es nur noch eine Klasse
Code:
class Event
{
public String whatHappened() { .... }
}
gibt, wo bei "whatHappened()" jeder das Reinschreiben kann, was er will?
Plakativ formuliert: Man könnte ja IMMER und wirklich für ALLES PropertyChangedEvents verwenden - warum überhaupt noch eigene Listener/Events, wenn damit alles abgebildet werden kann? Oder noch weiter getrieben: Wie wäre es, wenn man alle Listener-Interfaces und Events "deprecated", und es nur noch eine Klasse
Code:
class Event
{
public String whatHappened() { .... }
}
gibt, wo bei "whatHappened()" jeder das Reinschreiben kann, was er will?
Die "spärlichen" Antworten waren eben gerade die beiden in dem verlinkten Thread. Ich hatte eigentlich erwartet, dass da mehr Leute was dazu sagen können - ist ja eigentlich schon eine ziemlich allgemeine Frage....
EDIT: Und das mit der größe des Projektes ist so eine Sache: Projekte neigen dazu, nicht ihr Leben lang gleich groß zu bleiben - und bei einem Projekt mit 100 Klassen NUR PropertyChangeListener zu verwenden, und sobald es 110 Klassen sind alles auf Tysichere, dedizierte Listener umzustellen ist ja nicht praktikabel....