# Frage zur Anwendung des MVC-Konzept



## Eldorado (5. Jul 2010)

Hi,
ich habe das  mvc-Tutorial von sun durchgearbeitet und zu folgender Methode eine Frage:

```
protected void setModelProperty(String propertyName, Object newValue) {

        for (AbstractModel model : registeredModels) {
            try {

                Method method = model.getClass().
                        getMethod("set" + propertyName, new Class[]{
                            newValue.getClass()
                        });
                method.invoke(model, newValue);

            } catch (Exception ex) {
                //  Handle exception
            }
        }
    }
```
Hier werden alle model durchgegangen und die Methode "set" + property aufgerufen. Es besteht doch aber ein Problem, wenn man zwei Model drin hat, die beide die Methode z.Bsp. setName(String name); haben, jedoch diese beiden model nichts miteinander zu tun haben und so auch die Methoden etwas anderes machen. Wie kann man diese Problem umgehen, wenn man diese Architektur benutzen möchte?


----------



## Marco13 (5. Jul 2010)

Hmja... man könnte darüber philosophieren, wie allgemein anwendbar und praxistauglich dieses Modell wirklich ist. Im konkreten Fall könnte man noch die Klasse des Zielmodells übergeben...

```
class Auto implements Model { public void setName(String s) { ... } }
class Mensch implements Model { public void setName(String s) { ... } }

...
setModelProperty("Name", "Ferrari", Auto.class);
```
und in der Methode dann abfragen, ob das jeweilige Objekt von einer Klasse ist, die zur übergebenen passt. Ob das "schön" ist? ???:L Hm. :bahnhof:


----------



## Eldorado (5. Jul 2010)

Marco13 hat gesagt.:


> Hmja... man könnte darüber philosophieren, wie allgemein anwendbar und praxistauglich dieses Modell wirklich ist.


Ich hatte auch schon das Gefühl, dass das doch alles relativ umständlich ist. Ich möchte aber natürlich ein Konzept, dass die Daten und die Darstellung gut von einander abkoppelt, um später keine Probleme mit der Erweiterung bzw. der Veränderung zu haben. Für andere Umsetzungen bin ich voll und ganz offen :toll:


----------



## Marco13 (5. Jul 2010)

Naja, "umständlich" ist es eigentlich nicht, genaugenommen sogar sehr einfach. NOCH einfacher geht's eigentlich nur mit sowas wie swirrel - Project Hosting on Google Code (von 'Landei' hier aus dem Forum).

Aber genau der Link, den du im ersten Beitrag gepostet hattest, hatte mich damals zu der Frage veranlasst: http://www.java-forum.org/allgemein...rwendet-man-propertychangedevents-eigene.html ... Ich weiß nicht, ob das nicht nur ein Beispiel sein sollte, um zu verdeutlichen, welche Idee grundsätzlich hinter MVC steckt. Irgendwie kann ich mir kaum vorstellen, dass man in einer "echten" Anwendung mit 100 Modellklassen und 1000 "Properties" alles über String-Identifier und 'Object's lösen sollte.....


----------



## Eldorado (5. Jul 2010)

Wie löst du das denn bei deinen jetzigen Programmen? Schon mal danke für deine Engagement!


----------



## Michael... (5. Jul 2010)

Wenn ich mich mal zwischen rein reden darf ;-)

Ich mach mir immer vorher Gedanken wie Mein Model grundsätzlich aussehen soll und definiere dem entsprechend ein Interface, AbstractModel oder eine konkrete Klasse, die der Controller kennt.

Eine grössere Flexibiliät/Unabhängikeit des Controllers vom Model, kann ich bei der Vorgehensweise nicht wirklich erkennen. Wo ist da der Unterschied, ob der Controller die Implementierung/Schnittstelle des Models und somit seiner Methoden kennt oder nur die Methodennamen? In beiden Fällen muss, wenn das Model erweitert wird u.U. der Controller angepasst werden.

In dem Beispiel ist es sogar so, dass das Model den Controller (DefaultController) kennt, um auf dessen statische Variablen zu zugreifen. Das widerspricht ganz meiner Erwartung an ein MVC.


----------



## Marco13 (5. Jul 2010)

Ich persönlich tendiere eher dazu, spezifische Event/Listener-Klassen für spezifische Modellklassen zu schreiben. Wenn man ein größeres Projekt von vornherein plant (und nicht als jemand dient, der MVC in ein Projekt einbauen darf, das vorher 6 Jahre lang ohne solche Strukturen entwickelt wurde  ) dann würde sich mir die Frage aber wieder stellen: Wenn man 10 Modellklassen hat, die alle ein paar addXXX/removeXXX-Methoden haben, und überall "strukturell gleiche" Listener, Events und AbstractModel-Klassen auftreten wirkt das so repititiv (und verstößt eigentlich gegen das DRY-Prinzip). Generics können da in gewissen Grenzen helfen, aber sind nicht die Universalwaffe. Auch im verlinkten Thread kam ja keine definitive Antwort (wie auch...) - man muss das wohl von Fall zu Fall entscheiden. Wenn man wirklich ein Bean/Pojo hat, kann der PropertyChangeListener vielleicht sinnvoll sein, aber mir gefallen (subjektiv) die spezifischen Klassen besser.


----------



## maki (5. Jul 2010)

Ich finde das bei JFace Databinding sehr gut gelöst, die Modellklassen müssen nur konform zum JavaBeans PropertyChangeSupport sein, bietet sich für MVP an.
Wobei, es geht auch ohne PropertyChangeSupport.


----------



## Eldorado (5. Jul 2010)

@Michael: freue mich, wenn jemand reinredet 
Zu Thema Bohnen: da ich Hibernate für den Datenbankzugriff benutze, habe ich zwangsläufig Beans. In diesem Fall würden sich also PropertyChangeListener eignen. Wäre der Aufbau dann so, dass ich im Controller alle Bohnen in einer Liste ablege und jeweils einen Listener adde. Wenn dieser Anschlägt das event an die gui weitergebe und diese sich dann udated?


----------



## Eldorado (6. Jul 2010)

Wäre der beschrieben Aufbau so sinnvoll?


----------



## Marco13 (6. Jul 2010)

Mit Hibernate kenne ich mich nicht aus, und so remote und ohne genaue Kenntnis des Zusammenhangs zu beurteilen, ob ein Aufbau "sinnvoll" ist, ist immer schwierig. Es klingt erstmal(!) als würde da ziemlich viel vermischt, je nachdem worum es genau geht könnte man vielleicht versuchen, die Dinge irgendwie zu gruppieren... Vielleicht sowas wie

```
private List<Customer> customers = ...
private List<Product> products = ...

private void init()
{
    for (Customer c : db.getCustomers()) 
    {
        customers.add(c);
        c.addPropertyChangeListener(theListenerThatPassesEventsToCustomerView);
    }
    for (Product p : db.getProducts()) 
    {
        products.add(p);
        p.addPropertyChangeListener(theListenerThatPassesEventsToProductView);
    }
}
```
anstatt wirklich ALLES als "irgendeine bean" mit "irgendeinem PropertyChangeListener" in EINE Liste zu packen. Das ist KEIN Vorschlag, nur ein in-den-Raum-stellen der Möglichkeit (auf Basis dessen, was du bisher geschrieben hast). Ob das "sinnvoll" ist, kannst eigentlich nur du entscheiden 
(Als Beispiel: Wenn du irgendeine Super-Generische View hättest, die irgendeine Bean bekommt, und stupide mir reflection alle set-Methoden durchgeht und eine Ansicht erstellt mit
| property0NameLabel | property0ValueTextField |
| property1NameLabel | property1ValueTextField |
...
dann könnte man auch alles vereinheitlichen - aber vermutlich hast du das nicht, und vermutlich(!) würde es unübersichtlich werden, wenn man alles in einen Topf wirft...)


----------



## Eldorado (6. Jul 2010)

Ok, danke. Ich glaube ich werde mich mal dransetzen und was zeichnen, um einerseits den Aufbau nochmal zu überdenken und ihn euch verständlich zu machen.


----------



## Eldorado (6. Jul 2010)

OK, ich versuch es doch mit beschreiben, da ich beim Zeichnen so ne Niete bin^^
--------------------------------------
Das Haupt-Model, das Zugriff auf die UnterModel und somit deren Daten bietet. Es reicht PropertyChanges von den Unter-Models an die Listener des Haupt-Models weiter und feuert selbst welche, wenn neue Daten hinzukommen.

```
public class MainModel implements PropertyChangeListener{

    private PropertyChangeSupport propertyChangeSupport;
    private ArrayList<User> userList;
    private ArrayList<Product> productList;
    //etc.

    public MainModel() {
        propertyChangeSupport = new PropertyChangeSupport(this);
        userList = new ArrayList<User>();
        productList = new ArrayList<Product>();
        //Stelle Datenbankverbindung her
        //Lese Daten aus der Datenbank aus
    }

    public User getUser(int userId) {
        //User aus der Datenbank lesen und zurückgeben
    }

    public User addUser(User user) {
        //User in die Datenbank schreiben
        userList.add(user);
        user.addPropertyChangeListener(this);//Untermodel haben auch PropertyChangeSupport
        propertyChangeSupport.firePropertyChange(Data.USER.getIndetifier(), null, user);
    }

    //für Produkt etc. das selbe

    public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener){
        propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
    }

    public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener){
        propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
    }

    //Listener für events aus den dahinter liegenden Modeln werden "weitergegeben"
    public void propertyChange(PropertyChangeEvent evt) {
        propertyChangeSupport.firePropertyChange(evt);
    }
}
```

Das Enum Data beinhaltet alle Möglichen Daten:

```
ppublic enum Data {

    USER("user"),
    USER_ID("userId"),
    USER_NAME("userName"),
    USER_CATEGORY("userCategory"),
    USER_STATE("userState"),
    
    USERCATGEORY("usercategory"),
    USERCATEGORY_ID("usercategoryId"),
    USERCATEGORY_NAME("usercategoryName"),

    PRODUCT("product"),
    PRODUCT_ID("productId"),
    PRODUCT_NAME("productName"),
    PRODUCT_CATEGORY("productCaegory"),
    PRODUCT_STATE("productState"),
    PRODUCT_RETAIL_PRICE("productRetailPrice"),
    PRODUCT_PURCHASE("productPurchase"),

    PRODUCTCATEGORY("productcategory"),
    PRODUCTCATEGORY_ID("productcategoryId"),
    PRODUCTCATEGORY_NAME("productcategoryName"),

    SALE("sale"),
    SALE_ID("saleId"),
    SALE_DATE("saleDate"),
    SALE_USER("saleUser"),
    SALE_PRODUCT("saleProduct"),
    SALE_AMOUNT("saleAmount"),
    SALE_DATE_PRICE("saleDatePrice"),

    SETTLEMENT("settlement"),
    SETTLEMENT_ID("settlementId"),
    SETTLEMT_DATE("settlementDate"),
    SETTLEMENT_USER("settlementUser"),
    SETTLEMENT_MONEY("settlementMoney"),

    ORDER("order"),
    ORDER_ID("orderId"),
    ORDER_DATE("orderDate"),
    ORDER_PRODUCT("orderProduct"),
    ORDER_AMOUNT("orderAmount"),
    ORDER_PRICE("orderPrice");

    private Data(String indentifier) {
    }

    String getIndetifier(){
        return "";
    }
}
```

Und zu guter letzt die Gui:

```
public class Gui implements PropertyChangeListener{

    public Gui(MainModel model) {
        model.addPropertyChangeListener(this);
    }

    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(Data.USER.getIndetifier())) {
            //update
        }
        //...
    }

    public void addButtonDoSomethingListener(){
        //entweder cih mache einen Controller der tausende von Listener für 
        //alle Gui-Komponenten setzt oder ich behanlde die events in der 
        //gui und habe so halt viele kleine controller
    }
}
```

So wäre nett, wenn ihr kritische Kommentare abgebt und reinredet . Vielen Danke Leute!


----------



## Marco13 (6. Jul 2010)

Wie oben schon angedeutet würde ich _tendenziell_ versuchen, die einzelnen Dinge zu trennen: Also Listener für User, und Listener für Produkte, und Listener für das MainModel. Ich könnte mir vorstellen, dass das die ganze Sache flexibler macht. Wie die Abfragen nach den Identifiern (ohne n) in der GUI dann weiter aussehen würden, wäre wohl ganz spannend, aber dazu würde vermutlich (zu) viel GUI-Code gehören. 

Aber als GANZ grobes Beispiel: Wenn man aus dem GUI z.B. den SALE-Teil rausnehmen oder einen anderen hinzufügen will, oder das Verhalten sich etwas ändern soll, müßte relativ viel geändert werden - im speziellen das Data-enum und die (vermutlich schon sehr lange und unübersichtliche) propertyChange-Methode im GUI. 

Wie es jetzt ist, ist es zwar nicht das, was mal als "Gegenbeispiel für Lose Kopplung" bezeichnen würde (da nicht wirklich "gekoppelt", sondern eher Zusammengefasst wird), aber bei mir bleibt da doch das (subjektive!) Gefühl, dass vieles vermischt und verdengelt ist, und damit evtl. später Änderungen aufwändiger werden könnten. Ob das wirklich so ist, ist im Voraus schwer zu sagen. (Und vielleicht ist das so ja auch die ultimative Standard-Vorgehensweise, wenn man Beans aus einer Hibernate-DB holt? :bahnhof: ). Aber vielleicht hat ja noch jemand anderes Kommentare dazu, damit du nicht nur mein unfundiertes Gelaber lesen mußt


----------



## Eldorado (6. Jul 2010)

Ich bin grad dabei mir ein neues Konzept auszudenken(Ich bin dir dafür dankbar Marco, dass du mir Probleme aufzeigst und mich dadurch zwingst mir Gedanken zu machen. Da ist es mir egal, ob ich "unfundiertes Gelaber lesen muss").
Problem: Ich habe die Datenbankeinträge durch Hibernate ja als Objekte in meinem Programm. Werden jetzt die Daten eine dieser Objekte geändert, muss es wieder mit der Datenbank synchronisiert werden.
Möglich Lösung: Ich registriere im Haupt-Model einen DataChangedListener(Meinen eigenen Listener). Wenn dann event auftritt würde ich eine Datenbankverbindung herstellen und das Objekt synchronisieren.  
Frage: Ist das MVC-Konzeptionell in Ordnung?


----------



## Gast2 (6. Jul 2010)

Also ich finde das JFace Databinding auch Klasse...

Ich würde den Aufbau so gestalten:
Auf dem Client würde ich GUI/Controller/Model abtrennen.
Auf derm Server hast dan Services für das Transaktions Handling und BU Logik verantwortlich sind. Die greifen auf DAO Klassen zu, um deine Hibernate Pojos zu speichern und löschen usw.

Der Controller(oder Model) ruft die Remote Service vom Server auf und befüllt und validiert deine Models auf dem Client. Eventuell kannst deine Hibernate Pojos auch noch in ModelObjects wrappen damit du dort den ProprtyChangeSupport reinmachen kannst.

Um die Models und ModelObjects zu generieren höre ich immer wieder dass EMF schon viel mitbringt und viel Arbeit abnimmt(PropertySupport, Validierung usw.), hab mich leider selber noch nicht intensiv damit beschäftigt, wird aber bei nächster Gelegenheit nachgeholt ...


----------



## Eldorado (6. Jul 2010)

Ähm, entweder verstehst du mich falsch oder ich verstehe nicht was du meinst 
Ich habe keinen "Server", sondern nur einen Client, der über Hibernate auf eine MySQL-Datenbank(diese liegt logischerweise auf einem Server)(oder jede andere unterstützte Datenbank) zugreift und mit dem Daten arbeitet. Es geht mir zurzeit wirklich nur darum:


SirWayne hat gesagt.:


> Auf dem Client würde ich GUI/Controller/Model abtrennen.


Aber trotzdem Danke!


----------



## Gast2 (6. Jul 2010)

Was an der Schichten Trennung nichts ändern würde außer dass du kein Remote Zugriff hast...

MVC - Service - DAO - Hibernate POJOs


----------



## Eldorado (8. Jul 2010)

Ich poste euch mal meinen aktuellen Stand. Wenn ihr Lust habt, könnt ihr gerne euren Kommentar zu abgeben. Ich will auch aber auch nicht zuviel Arbeit machen hier ständig meine Ansätze durchzuschauen. Auf jeden Fall schon mal Danke an alle :toll:. Zum Thema JFace: Werde ich mir mal bei Gelegenheit genauer betrachten.

Ich habe jetzt eine generische DAO-Klasse geschrieben, die sich um die Datenbankkommunikation und das feuern von Events kümmert:

```
public class GenericDAO<T extends IndexableData> {

    private Session session;
    private Class<T> entity;
    private ArrayList<DataChangedListener> listeners;

    public GenericDAO(Session session) {
        this.session = session;
        this.entity = (Class<T>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
        listeners = new ArrayList<DataChangedListener>();
    }

    protected Session getSession() {
        if (session != null) {
            return session;
        }
        throw new IllegalStateException("Session has not been set.");
    }

    public List<T> getAll() {
        return getSession().createCriteria(entity).list();
    }

    public T loadById(int id) throws HibernateException{
        return (T) getSession().load(entity, id);
    }

    public void save(T entity) {
        getSession().save(entity);
        fireDataCahngedEvent(new DataChangeEvent(entity, Events.ADD));
    }

    public void update(T entity){
         getSession().update(entity);
        fireDataCahngedEvent(new DataChangeEvent(entity, Events.UDPATE));
    }

    public void delete(T entity) {
        getSession().delete(entity);
        fireDataCahngedEvent(new DataChangeEvent(entity, Events.REMOVE));
    }

    public void addDataChangedListener(DataChangedListener listener){
        listeners.add(listener);
    }

    public void removeDataChangedListener(DataChangedListener listener){
        listeners.remove(listener);
    }

    private void fireDataCahngedEvent(DataChangeEvent event){
        for (DataChangedListener dataChangedListener : listeners) {
            dataChangedListener.dataChanged(event);
        }
    }
}
```
IndexableData ist ein einfaches Interface, das getter und setter für eine id bereitstellt. Die Gui(und der Controller) holen sich die DAOs aus einer DaoFactory. Dies ist auch die einzige Klasse die für ein neues Daten-POJO angepasst werden muss:

```
public class DAOFactory {

    public static final int USER = 0;
    public static final int USER_CATEGORY = 1;
    public static final int PRODUCT = 2;
    public static final int PRODUCT_CATEGORY = 3;
    public static final int SALE = 4;
    public static final int SETTLEMENT = 5;
    public static final int ORDER = 6;
    private static GenericDAO[] dAOs;

    public DAOFactory() {
        DatabaseAdapter da = new DatabaseAdapter(null);
        dAOs = new GenericDAO[7];
        dAOs[0] = new GenericDAO<User>(da.getSession());
        dAOs[1] = new GenericDAO<UserCategory>(da.getSession());
        dAOs[2] = new GenericDAO<Product>(da.getSession());
        dAOs[3] = new GenericDAO<ProductCategory>(da.getSession());
        dAOs[4] = new GenericDAO<Sale>(da.getSession());
        dAOs[5] = new GenericDAO<Settlement>(da.getSession());
        dAOs[6] = new GenericDAO<Order>(da.getSession());
    }

    public GenericDAO getDAO(int key) {
        return dAOs[key];
    }
}
```

Wird ein neues Element vom Controller hinzugefügt oder verändert wird das entsprechende event gefeuert. Die Gui muss dann nur noch zwischen add, update, delete switchen(Enum).

```
public class DataChangeEvent extends EventObject {

    private Events eventType;

    public DataChangeEvent(IndexableData source, Events eventType) {
        super(source);
    }

    public Events getEventType() {
        return eventType;
    }

    public int getId(){
        IndexableData temp =  (IndexableData)getSource();
        return temp.getId();
    }
}
```


```
public interface DataChangedListener extends EventListener {

     public void dataChanged(DataChangeEvent event);

}
```

Da jeder "Datentyp" seinen eigenen Listener hat fällt das switchen zwischen den verschiedenen "Datentypen" im Listener weg und so ist eine Anpassung auch nicht aufwendig. ich hoffe, dass ich jetzt auf einem besseren weg bin. Wenn nicht, dürft ich mich auch gerne wieder dazu veranlassen den Papierkorb aufzumachen . Ich habe damit kein Problem, da man ja bekanntlich aus Fehlern lernt


----------



## Gast2 (8. Jul 2010)

Ich würde die Events nicht in den DAO Klassen machen...


----------



## Eldorado (8. Jul 2010)

1. Warum nicht?
2. Wo dann?
Wäre nett, wenn du das noch kurz näher erläutern könntest. Danke.


----------



## Gast2 (8. Jul 2010)

Also ich würde sowas in keiner Datenbank Zugriff Klasse machen... Sonst musst du die ja irgendwo regisitieren und ein DAO ist KEIN Model... Darum würde ich es nicht dort ansiedeln wenn ich heut daheim bin kann ich ja ein kleines Bsp. wie ich es machen würde...

Aber warte mal was maki dazu sagt ich glaub er hat da mehr Erfahung als ich


----------



## Eldorado (8. Jul 2010)

OK, danke!


----------



## maki (8. Jul 2010)

> Aber warte mal was maki dazu sagt ich glaub er hat da mehr Erfahung als ich


Naja.. Hab ehrlich gesagt weder mit Swing noch mit SWT/JFace im zusammenhang mit DBs gearbeitet wo PropertyChange direkt aus dem Model gefeuert wurden, hatten immer ein eigenes Model für die View auf Rich Clients, welches entweder das "echte" Model gewrapped hatte oder dazu konvertiert wurde, Fowler nennt das Presentation Model, hab hier im Forum letztens den Begriff "Model View Adapter" von Ebenius gehört.

Muss SirWayne zustimmen, würde das nicht alles ins DAO packen (würde es noch nicht mal in richtige Domänenmodell packen).

Wenn ein Datensatz/Entity in Bearbeitung ist, wird dieser gelockt(!), damit gibt es keine parallelen Änderungen, wäre auch sehr schlecht für die Konsistenz 
Dazu gibt es 2 grundlegende Ansatze, Optimistic und Pessemistic locking (auch da hat fowler einen Artikel zu ), ersteres wird durch JPA/Hibernate/EclipseLink etc. direkt unterstützt (@Version Annotation) und üfrht dazu, das erst beim Speichern festgestellt (per Exception) wird ob man eine "Stale" Entity hatte.
Bei letzterem wird er Datensatz direkt in der DB gelockt (zB. select for update), je nach DB unterscheidet sich das schon mal ...
Dann gibt es noch die "Offline" Abwandlungen dazu, d.h. die Anwendung selber verwaltete das Pessemistic bzw. Optimistic Locking und ist dadurch unabhängig von der DB.

PropertyChangeSupport haben wir ausschliesslich genutzt, um das (Presentation-)Model mit der View zu synchronisieren bzw. umgekehrt.


----------



## Eldorado (8. Jul 2010)

Verbindung von dem Presentation Model und der view ist klar. Aber wie verbindet man das Model mit dem Presentation Model ohne den ganzen Code doppelt und dreifach zu schreiben. Du hast gesagt:


> das "echte" Model gewrapped hatte oder dazu konvertiert wurde


Die Verbindung von den POJOS - DAO - Presentation Model ist mir noch nicht so ganz klar. Könntest du da vielleicht noch ein bisschen Licht ins dunkle bringen.


----------



## maki (8. Jul 2010)

Versuche es mal 

Der generelle Ablauf sieht ungefähr so aus:
View -> Service -> DAO -> DB

DAO hat rein gar nix mit dem PresentationModel zu tun, das DAO gibt (u.a.) eine Entity zurück, dieses kann ein POJO sein.
Die View kennt das PresentationModel, dieses ist anders als die eigentliche Entität, hat zB. spezielle Eigenschaften die nur für diese Views gebraucht werden (zB. PropertyChangeSupport, IAdabtable etc. pp.).
In einfachen Fällen reicht ein Wrapper die von der View um die Entities "gestülpt" werden, in schwierigeren braucht es TransferObjekte, die vom Service zurückgegeben werden und von der View in das PresentationModel konvertiert werden (und natürlich zurück).

Kandidaten für Redundanz sind zB. Validierung, wobei die View meist nur eifnache Validierung durchführen kann (Feld darf nicht leer sein bzw muss eine Zahl enthalten), komplexe Sachen werden dann "hinter" dem Service validiert.


----------



## Eldorado (8. Jul 2010)

Verstehe ich das Richtig: Es gibt 1 PresentationModel, das die View kennt um mit dem die View kommuniziert. So jetzt habe ich ganz viele Pojos, die ich über das Dao bekomme und jetzt ...?
Ich verstehe nicht was mit wrappen gemeint ist, bzw wie das umgesetzt wird...
Abermals danke für deine Hilfe.


----------



## maki (8. Jul 2010)

> Verstehe ich das Richtig: Es gibt 1 PresentationModel, das die View kennt um mit dem die View kommuniziert. So jetzt habe ich ganz viele Pojos, die ich über das Dao bekomme und jetzt ...?


Nicht nur ein einziges, sondern je nach View und wie es gebraucht wird.



> Ich verstehe nicht was mit wrappen gemeint ist, bzw wie das umgesetzt wird...


Das Adapter Muster, für die View braucht dein PresentationModel bestimmte Dinge, die deswegen nicht zur Entity gehören, weil diese sonst Abhängigkeiten zur View Techn. hätte.
Du hast jetzt zB. Abhängkeiten zur View techn. in deinem DAO & und deinen POJOs.
Entkoppeln bedeutet eben Aufwand, dafür schafft es klarheit


----------



## Eldorado (8. Jul 2010)

maki, ich habe eine Idee wie wir vielleicht schneller fertig werden: Das Forum hat doch einen TeamSpeak 2 Server. Wenn du Lust hättest könnte wir uns ja da miteinander unterhalten?


----------



## Eldorado (8. Jul 2010)

Zurzeit sind doch Pojos und DAO  völlig unabhängig zur View. Du kannst jede View daraufpacken, diese würde sich ihre Daten holen und sich für Änderungen registrieren. Natürlich ist die View abhängig von einem Model das das Interface implementiert hat, aber das ist doch denke ich normal...
Das PresentationModel ist aber immer anhängig zur View(logischerweise). Kannst du vielleicht nur mal ganz kurz ein Beispiel geben, wie ich meine Entity in PresentationModel wrappe? Sorry, das ich wahrscheinlich grade total schwer von Begriff bin -.-


----------



## maki (8. Jul 2010)

Du schreibst einfach eine Klasse, die eine Entity aufnimmt (zb. per Konstruktor), und dann Aufrufe auf den Wrapper an die Entity delegiert (get.., set...), da kannst du zB. auch deine Events abfeuern, wenn die Entity zB. keinen PropertyChangeSupport bietet oder haben kann/darf.

Vielleciht habe ich mal wieder auch nur zu kompliziert gedacht 
Es geht auch ohne Wrapper, wenn es denn wirklich nur der PropertyChangeSupport ist um den es geht.
Diesen würde ich nicht im DAO machen, das eigentliche Binding der Listeners an die Properties kann man ja auch schön auslagern (in einen Presenter, siehe MVP).

Denk dir nix, kann manchmal ganz schön vertrackt sein das ganze.

Bin heute Abend unterwegs und ab morgen nachmittag im Urlaub (davor auf Arbeit), denke nicht dass ich da telefonieren kann


----------



## Gast2 (9. Jul 2010)

Hier hab ich mal einen möglichen einfachen Aufbau einer Anwendung dargestellt(siehe jar): Die View ist sehr einfach gehalten

Also Views:


```
package view;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main extends JFrame{
	
	public Main(){
		super("Beispiel MVC");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JPanel createView = new CreateView();
		JPanel listView = new ListView();
		JPanel lastView = new LastView();
		
		add(createView, BorderLayout.SOUTH);
		add(listView, BorderLayout.CENTER);
		add(lastView, BorderLayout.NORTH);
		
		pack();
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				new Main().setVisible(true);	
			}
		});
	}

}
```


```
package view;

import java.awt.Dimension;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;

import controller.CreateKundeAction;
import db.Kunde;
import db.KundeHibernateImpl;

public class CreateView extends JPanel{

	private CreateKundeAction action;
	private JTextField  field;
	public CreateView(){
		super();
		setBorder(BorderFactory.createTitledBorder("Kunde anlegen"));
		action = new CreateKundeAction();

		field = new JTextField();
		field.setPreferredSize(new Dimension(100,25));
		add(field);
		add(new JButton(new AbstractAction("Anlegen") {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				//Hier ne Factory oder Sosntiges benutzen
				Kunde k = new KundeHibernateImpl();
				k.setName(field.getText());
				action.setNewKunde(k);
				action.run();
				field.setText("");
			}
		}));
	}
}
```


```
package view;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import controller.CreateKundeAction;

public class ListView extends JPanel implements PropertyChangeListener{

	private CreateKundeAction action;
	private DefaultListModel listModel;
	private JList jList;
	
	public ListView(){
		super(new FlowLayout());
		setBorder(BorderFactory.createTitledBorder("Alle Kunden"));
		action = new CreateKundeAction();
		action.getKundenList().addPropertyChangeListener(this);
		listModel = new DefaultListModel();
		jList = new JList(listModel);
		jList.setPreferredSize(new Dimension(150,150));
		add(new JScrollPane(jList));	
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if(evt.getPropertyName().equals("KUNDE_ADD")){
			listModel.addElement(evt.getNewValue());
		}
		
	}
}
```


```
package view;

import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;

import controller.CreateKundeAction;

public class LastView extends JPanel implements PropertyChangeListener{

	private CreateKundeAction action;
	private JLabel jLabel;
	
	public LastView(){
		super();
		setBorder(BorderFactory.createTitledBorder("Letzter Kunde"));
		action = new CreateKundeAction();
		action.getKundenList().addPropertyChangeListener(this);
		jLabel = new JLabel();
		add(jLabel, BorderLayout.CENTER);	
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if(evt.getPropertyName().equals("KUNDE_ADD")){
			jLabel.setText(evt.getNewValue().toString());
		}
		
	}
}
```

Controller Klassen

```
package controller;

public abstract class Action {
	
	public abstract void run();

}
```


```
package controller;

import service.KundenService;
import service.KundenServiceImpl;
import model.KundenModel;
import db.Kunde;

public class CreateKundeAction extends Action{

	//hab ich alles  mit Spring injected
	private static KundenModel kundenList = new KundenModel();
	private KundenService kundenService = new KundenServiceImpl();
	private Kunde newKunde;


	@Override
	public void run() {
		kundenService.createKunde(newKunde);
		kundenList.addKunde(newKunde);
	}

	public KundenModel getKundenList() {
		return kundenList;
	}


	public void setNewKunde(Kunde newKunde) {
		this.newKunde = newKunde;
	}



	public Kunde getNewKunde() {
		return newKunde;
	}

}
```

ModelKlasse

```
package model;


import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;

import db.Kunde;


public class KundenModel  {
    
    private List<Kunde> kundeList = new ArrayList<Kunde>();
	protected transient PropertyChangeSupport listeners = new PropertyChangeSupport(this);
    
    
    
    /**
     * Adds a property-change listener.
     * @param l the listener
     */
    public void addPropertyChangeListener(PropertyChangeListener l){
        if (l == null) {
            throw new IllegalArgumentException();
        }
        this.listeners.addPropertyChangeListener(l);
    }
    
    
    public void removePropertyChangeListener(PropertyChangeListener l){
        this.listeners.removePropertyChangeListener(l);
    }
    
    /**
     * Notificates all listeners to a model-change
     * @param prop the property-id
     * @param old the old-value
     * @param newValue the new value
     */
    protected void firePropertyChange(String prop, Object old, Object newValue){
        if (this.listeners.hasListeners(prop)) {
            this.listeners.firePropertyChange(prop, old, newValue);
        }
    }

    public void addKunde(Kunde o) {
        this.kundeList.add(o);
        // model has changed --> fire
        firePropertyChange("KUNDE_ADD", null, o); //$NON-NLS-1$
    }
    
    public void remove(Kunde o) {
        this.kundeList.remove(o);
//      model has changed --> fire
        firePropertyChange("KUNDE_REMOVE", o, null); //$NON-NLS-1$
    }
    
    public List<Kunde> getKundeList() {
		return new ArrayList<Kunde>(kundeList);
	}


}
```

So das wäre dein MVC:

Jetzt kommen deine Hibernate(fehlen die Annotations) Pojos:

```
package db;

public interface Kunde {

	Long getID();
	
    void setName(String name);

	String getName();

}
```


```
package db;

public class KundeHibernateImpl implements Kunde {

	private String name;

	@Override
	public Long getID() {
		// TODO Auto-generated method stub
		return null;
	}
	
	/* (non-Javadoc)
	 * @see dao.Kunde#setName(java.lang.String)
	 */
	@Override
	public void setName(String name) {
		this.name = name;
	}

	/* (non-Javadoc)
	 * @see dao.Kunde#getName()
	 */
	@Override
	public String getName() {
		return name;
	}
	
	@Override
	public String toString() {
		return name;
	}


}
```

Der Service, dort kommt u.a. deine BuLogik und Transaktionshandlling rein, welcher oben im Controller ausgeführt wird:

```
package service;

import java.util.List;

import db.Kunde;

public interface KundenService {

	List<Kunde> getKunden();

	void createKunde(Kunde kunde);

}
```


```
package service;

import java.util.List;

import dao.HibernateKundenDAOImpl;
import dao.KundenDAO;
import db.Kunde;

public class KundenServiceImpl implements KundenService{
        
		//Hab ich immer mit Spring injected
//		private KundenDAO kundenDAO;
	
	private KundenDAO kundenDAO = new HibernateKundenDAOImpl();
	
        /* (non-Javadoc)
		 * @see service.KundenService#getKunden()
		 */
        @Override
		public List<Kunde> getKunden(){
            return kundenDAO.getAllKunde();
        }
        
        /* (non-Javadoc)
		 * @see service.KundenService#createKunde(dao.Kunde)
		 */
        @Override
		public void createKunde(Kunde kunde){
        	kundenDAO.createKunde(kunde);
        }
        
}
```

DAO Klassen für den DB Zugriff:


```
package dao;

import java.util.List;

import db.Kunde;

public interface KundenDAO {

	Kunde createKunde(Kunde kunde);

	List<Kunde> getAllKunde();

}
```


```
package dao;

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

import db.Kunde;

public class HibernateKundenDAOImpl implements KundenDAO{
 
//    private SessionFactory sessionFactory;
    
    /* (non-Javadoc)
	 * @see dao.KundenDAO#createKunde(db.Kunde)
	 */
    @Override
	public Kunde createKunde(Kunde kunde) {
    	System.out.println("Speicher Kunde "+ kunde+ "in der DB ab");
//        return (Kunde) sessionFactory.getCurrentSession().save(KundeHibernateImpl.class, kunde);
       return null; 
    }
    /* (non-Javadoc)
	 * @see dao.KundenDAO#getAllKunde()
	 */
    @Override
	public List<Kunde> getAllKunde() {
//        Criteria crit = sessionFactory.getCurrentSession().createCriteria(KundeHibernateImpl.class);
//        return crit.list();
    	return new ArrayList<Kunde>();
    }
}
```


So mal für den Anfang ^^... Vielleicht kann maki nach seinem Urlaub noch bessere Vorschlähe bringen


----------



## Eldorado (9. Jul 2010)

Vielen Dank Sir Wayne dass du dir so Arbeit gemacht hast! Ich bin grade dabei auf Linux umzusteigen und werde mir dein Beispiel heute Abend mal zu Gemüte führen.


----------



## Gast2 (9. Jul 2010)

Wie gesagt ich so einfach wie möglich gehalten... Die Models und Service und DAOs hab ich eigentlich immer mit Spring injected und irgendein Databining würde ich in der GUI auch empfehlen...
Understanding JFace data binding in Eclipse, Part 1: The pros and cons of data binding


----------

