# Wie weiter gehen und tutorial für MVC



## seejay (25. Nov 2007)

Hallo,
ich möchte ein Programm schreiben und dieses Mal von anfang an den richtigen weg und kein Bastelprogramm haben. Jetzt ist die Frage wie ich weiter vorgehen soll. Zu aller erst habe ich mir aufgeschrieben, was das Programm alles können soll, ohne daran zu denken, wie ich es implementieren könnte. Was sollte ich jetzt als nächstes tun? Oberflächendesign? UML Diagramme? Wie geht ihr vor oder gibt es irgendwo gute Skripte/Tutorials bei denen es an einem Beispiel von anfang an gemacht wird? Java ist auch eine Insel bin ich bereits am durchlesen, leider teilweise etwas langweilig, da ich zu beginn etwa 95% der Sachen schon kenne. Aber egal ich les trotzdem mal alles weiter.

Und ich würde auch gleich dazu das MVC Konzept lernen und einsetzen, nur leider finde ich nichts gescheites. Der Text in Wiki hat mir nix gebracht, es wäre wieder gut, wenn jemand ein Tutorial mit Beispiel kennen und mir nennen könnte.

Vielen Dank und Gruß
seejay


----------



## stevieboy (26. Nov 2007)

Oberflächendesign als nächsten Schritt? Ich denke mal eher nicht 

Im MVC-Pattern zu programmieren ist nicht sonderlich schwer: Du musst Dir nur überlegen, welche Aufgabe Dein gerade zu schreibender Code hat:

- Hält er Daten bzw. ermöglicht den Zugriff auf diese -> Model
- Zeigt er Daten an bzw. beinhaltet er Code, der sich von Darstellungsart zu Darstellungsart ändert (Swing-GUI, Web, Console)  -> View
- der Controller sitzt in der Mitte und kennt sowohl das GUI als auch Modelobjekt.

Datenanfragen werden immer vom Controller an das Model weitergereicht, dort verarbeitet und beantwortet und an den Controller zurückgegeben. Dieser schickt dann die Antwort an den View.

Dein Ziel muss es sein, dass Du zum Beispiel das Model austauschen kannst (von Datei zum SQL-Tabelle wechseln zB), ohne dass Du eine einzige Zeile in deiner Gui austauschen und nur minimale Änderungen im Controller machen musst. Das Gleiche gilt für die Umstellung auf eine andere GUI, das Model bleibt davon unberührt.

Wenn Du das schaffst, hast Du ein lupenreines, rubustes MVC-Programm geschrieben.

PS: Für gute Links sind hier andere zuständig, sorry ich hab keine.


----------



## seejay (26. Nov 2007)

stevieboy hat gesagt.:
			
		

> ...
> Im MVC-Pattern zu programmieren ist nicht sonderlich schwer: Du musst Dir nur überlegen, welche Aufgabe Dein gerade zu schreibender Code hat:
> 
> - Hält er Daten bzw. ermöglicht den Zugriff auf diese -> Model
> ...



Und hier liegt mein Problem, ich kann mir des ohne Beispiel nicht richtig vorstellen. Also wenn jemand ein kleines Beispielprogramm oder ein tutorial zur hand hätte wäre dies prima.

Also bitte her mit den Links


----------



## HLX (28. Nov 2007)

Starte doch mal einen einfachen kleinen Versuch in Swing oder JSP/Servlet und stell ihn hier rein. Dann sehen wir, was schon an Verständnis da ist und korrigieren den Rest.


----------



## seejay (29. Nov 2007)

ok, denke ich habs jetzt verstanden. Hier mein Versuch:
Klasse User

```
public class User {
    
    private String name = "Test";
    private int gebjahr = 1990;
    
    /** Creates a new instance of User */
    public User() {
    }
    
    /**
    * @return Rückgabe Geburtstagsjahr.
    */ 
    public int getGebjahr() {
        return gebjahr;
    }

    /**
    * @return Rückgabe Name.
    */ 
    public String getName() {
        return name;
    }

   /**
    * @param gebjahr Geburtsjahr.
    */    
    public void setGebjahr(int gebjahr) {
        this.gebjahr = gebjahr;
    }

   /**
    * @param name Name.
    */    
    public void setName(String name) {
        this.name = name;
    }
```

Klasse UserGui

```
public class UserGui extends javax.swing.JFrame implements Observer{
    
    private UserControllable controller;
    /** Creates new form UserGui */
    public UserGui() {
        initComponents();
    }
    
    public UserGui( UserControllable controller ) {
      super( "UserController" );
      this.controller = controller;
      setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      initComponents();
      pack();
      setVisible( true );
   }    
 
    private void jbtnOKJahrActionPerformed(java.awt.event.ActionEvent evt) {                                           
        controller.changeJahr(Integer.valueOf( jtfJahr.getText() ).intValue());
    }                                          

    private void jbtnOKNameActionPerformed(java.awt.event.ActionEvent evt) {                                           
        controller.changeName(jtfName.getText());
    }
```


```
public interface UserControllable {
    
   public void changeName(String name);
   public void changeJahr(int jahr);
    
}
```


```
public class UserController extends Observable implements UserControllable{

    private User user;
    /** Creates a new instance of UserController */
    public UserController() {
        UserGui viewer = new UserGui( this );
        addObserver( viewer );
        viewer.setVisible(true);
        user = new User(); 
    }

    public void changeName(String name) {
        user.setName( name );
        setChanged();
        notifyObservers( user );         
    }

    public void changeJahr(int jahr) {
        user.setGebjahr( jahr );
        setChanged();
        notifyObservers( user );
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new UserController();
    }    
    
}
```

Könntet ihr mal drüber sehen und so etwa müsste ich es doch auch machen, um Daten für die Klasse User aus ner Textfile oder SQL zu laden? Also ne Unterklasse, die die Daten läd und dann an User weitergibt?!


----------



## stevieboy (29. Nov 2007)

Ok, das sieht schon sehr gut aus.

Noch ein paar Hinweise, wie ich es weiterentwickeln/optimieren würde:

1. Der Standardkonstruktur der UserGUI ohne Parameter macht wenig Sinn, denn eine GUI ohne Controller (und damit ohne Daten) nützt ja nichts. Entweder du lässt ihn einfach weg, was zur Erstellung eine GUI-Objektes halt immer einen Controller erfordert oder du nutzt Standardwerte im parameterlosen Konstruktor und rufst damit den parameterisierten Konstruktor auf. Im Falle der GUI tendiere ich zur ersten Variante, da es unsauber wäre, wenn ein GUI-Objekt einen Controller erzeugt.

2. In der Klasse User solltest Du deine Standard-Testwerte nicht als Initialisierung angeben sondern eher den Konstruktur in Parameter zwingen und das UserObjekt dann damit erstellen:

```
public class User {
   
    private String name = new String();
    private int gebjahr = 0;
   
    /** Creates a new instance of User */
    public User(String name, int gebJahr) {
          this.name=name;
          this.gebjahr=gebJahr;
    }

    /** Creates a new instance of User with default values */
    public User() {
         //calls the parameterized constructor
         this("Test",1990);
    }  
   //...
```

Mit dieser Lösung hast Du immer ein "funktionierendes" Objekt.

Das kann man auch allgemein zu Konstruktoren sagen: Gib nur Konstruktoren an mit den man für Deine Applikation sinnvolle Objekte erstellen kann. Ich bin hier zB der Meinung, dass ein leeres User-Objekt keinen Sinn macht.

3. Die Lesbarkeit des Codes erhöht sich, wenn Du interne Klassenvariablen und -methoden immer mit this referenzierst. Das heißt also z.B. statt

```
public void changeJahr(int jahr) {
        user.setGebjahr( jahr );
        setChanged();
        notifyObservers( user );
    }
```

eher


```
public void changeJahr(int jahr) {
        this.user.setGebjahr( jahr );
        this.setChanged();
        this.notifyObservers( this.user );
    }
```

Dadurch kannst Du leichter zwischen Klassenvariablen und temporären Variablen wie zB hier "jahr" unterscheiden.

4. Der Controller sollte sich die GUI als interne Variable merken, um eventuell Updates abzusenden.

5. Ich denke es wäre sauberer, wenn ein Klick auf das Schließen-Kreuz in der GUI nicht direkt die Anwendung beendet (EXIT_ON_CLOSE). Lieber sollte eine exit-Methode im Controller aufgerufen werden (ins Interface aufnehmen!). Dann kannst Du leichter Abschlussarbeiten wie "sichern in SQL" usw. implementieren.

Soviel erstmal zu meinem Senf. Nun zu Deiner Daten-Frage:

Es ist auf zweierlei Weise möglich,das zu implementieren.

Zum einen könntest Du das in der Model-Klasse (User) direkt einbauen. Dann lädt sich das Model während eines inits die Daten aus der Datenbank.

Zum anderen - und das ist der Weg für größere Projekte - kann ein sog. DAO (Data Access Object) erstellt werden. Diese Klasse würde sämtliche Datenzugriffe kapseln und Daten an das Model zurückliefern. Um unterschiedliche Speichermöglichkeiten leicht zu implementieren (XML, MySQL, MS-SQL, flat file, etc) könnte man auch hier dem Model nur ein DAO-Interface bekannt machen,welches dann von DAOMySQL usw. implementiert wird.

So, das war ja eine ganze Menge. Vieles ist aus meiner persönlichen Erfahrung gewachsen und daher eben auch subjektiv. Manche Dinge - wie zB. Punkt 3 sind heiße Eisen, weil manche Leute es unnötig finden. Insofern: Pick Dir die  Tips raus, die Du sinnvoll findest!

Bei Fragen fragen.

LG

Stevie


----------



## seejay (29. Nov 2007)

Vielen Dank schon einmal, hab so gut wie alles übernommen. Nur wegen schließen muss ich nochmal genauer googeln, grad nix gefunden

Zu dem Teil unten hab ich noch ne Frage.



			
				stevieboy hat gesagt.:
			
		

> Zum einen könntest Du das in der Model-Klasse (User) direkt einbauen. Dann lädt sich das Model während eines inits die Daten aus der Datenbank.
> 
> Zum anderen - und das ist der Weg für größere Projekte - kann ein sog. DAO (Data Access Object) erstellt werden. Diese Klasse würde sämtliche Datenzugriffe kapseln und Daten an das Model zurückliefern. Um unterschiedliche Speichermöglichkeiten leicht zu implementieren (XML, MySQL, MS-SQL, flat file, etc) könnte man auch hier dem Model nur ein DAO-Interface bekannt machen,welches dann von DAOMySQL usw. implementiert wird.
> 
> ...



Vor allem den letzten Teil verstehe ich nicht so. Model nur ein DAO Interface bekannt machen...
Würde doch dann so aussehen:

```
public interface DAO {
    
   public String getName(int id);
   public int getJahr(int id);
    
}
```


```
public class User implements DAO{
...
```

Dann müsste ich bei einem Wechsel doch auch wieder alles ändern oder?


----------



## maki (29. Nov 2007)

> Dann müsste ich bei einem Wechsel doch auch wieder alles ändern oder?


Wenn du ein Dao Interface und eine DAO implementierung hast, dann musst du nur die implementierung ändern/neuschreiben.
Auf jedenfall solltest du *nicht* User das DAO interface implmentieren lassen!


----------



## stevieboy (29. Nov 2007)

seejay hat gesagt.:
			
		

> ```
> public class User implements DAO{
> ...
> ```
> ...



Neee, nicht so!

Die Klasse User hat eine interne Klassenvariable:

```
public class User{

         private DAO dataHandler;

private init(){
   this.dataHandler= new MySQLDAO(); //eventuell mit db-daten wie dbname und user/pw als Parameter
}
//...
```

und dazu die passende


```
public class MySQLDAO implements DAO{
 //MySQL-spezifische Implementierungen des DAO-Interfaces
}
```


Jetzt klar?

So kannst Du beliebig viele DAO-Arten erstellen und musst nur in der Init das erzeugte Objekt ändern, alles andere bleibt gleich.


----------



## stevieboy (29. Nov 2007)

@maki
Mist ich schreib zu lang.


----------



## seejay (30. Nov 2007)

```
public interface DAO {

    public void connect(String user, String pw, String host);
    public void connect(String user, String pw);
    public ResultSet getRSselect(String statement);
    public void doQuery(String statement);
    public void close();
}
```

Also meint ihr des so?

Ist des so in Ordnung?

Gruß
seejay


----------



## maki (30. Nov 2007)

Wozu soll denn das connect() gut sein???

Das ist keine Methode die dem Client zur Verfügung stehen sollte!

Solche Dinge müsse in einem sauberen Design alle in der DAO Schicht erledigt werden.


----------



## seejay (30. Nov 2007)

dachte jetzt, dass die DAO Schicht. z.b. MySql ja passwort und benutzer braucht und um es später bzw andersweitig zu benutzen, kann ich so einfach die Daten ändern und muss sie nicht in der MySQL Klasse ändern, wo sie doch sonst als Konstanten drinstehen würden.


----------



## maki (30. Nov 2007)

seejay hat gesagt.:
			
		

> dachte jetzt, dass die DAO Schicht. z.b. MySql ja passwort und benutzer braucht und um es später bzw andersweitig zu benutzen, kann ich so einfach die Daten ändern und muss sie nicht in der MySQL Klasse ändern, wo sie doch sonst als Konstanten drinstehen würden.


Das die DAO Schicht die Verbindungsdaten zur DB braucht ist richtig 
Nicht richtig ist allerdings, das du in deiner Geschäftslogik die Verbindungsdaten zur DB hinterlegst... :noe: 

Dafür kann man Konfigurationsdateien verwenden, auch sollten deine DAOs von einer Factory erzeugt werden und nicht (!!!) vom Client per Konstruktor.


----------



## guestUser (30. Nov 2007)

wie macht man das denn, wenn ein user die verbindungsdaten über die gui eingibt?


----------



## maki (30. Nov 2007)

> wie macht man das denn, wenn ein user die verbindungsdaten über die gui eingibt?


Selbst dann übergibt man diese Daten an die Factory, aber doch nicht an jedes einzelne DAO.
Oder man schreibt diese Daten gleich in die Konfig Dateien (zB. Property files).


----------



## seejay (30. Nov 2007)

wäre es nicht besser ein Singleton zu benutzen? Da ich doch nur eine Verbindung habe, wenn ich jetzt mal von meinem Normalfall ausgehe.
Bzw. gehe ich recht in der Annahme, dass Factory eine Art Singleton ist, dass aber mehrere erstellen kann?


----------



## maki (30. Nov 2007)

Eine DaoFactory wird sehr oft als Singleton implementiert 

Deine Daos bekommst du dann so ähnlich:

DaoFactory.getInstance().getUserDao();

Für den Einstieg wäre das gut genug, gibt natürlich bessere und flexiblere Möglichkeiten, sind aber etwas komplexer, erstmal Schritt für Schritt.

Dazu gibt es wirklich sehr viele Ressourcen im inet 

http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html


----------



## seejay (3. Dez 2007)

also ich hab da jetzt so meine Probleme. Bisher habe ich dies:

```
public interface DAO {

    public ResultSet getRSselect(String statement);
    public void doQuery(String statement);
    public void close();
}
```


```
public class MySQLDAO implements DAO{ 
    
    private MySQLDAO instance = null;
    
    /** Creates a new instance of MySQLDAO */
    protected MySQLDAO() {
    }

    public MySQLDAO getInstance()
    {
        if (instance==null)
        {
            instance = new MySQLDAO();
        }
        return instance;
    }
    
    public ResultSet getRSselect(String statement) {
    }

    public void doQuery(String statement) {
    }

    public void close() {
    }
```

1. Wie man sieht fehlt im interface DAO die Methode getInstance, da ich nicht weiß wie ich die da mit implementieren soll, da sie ja je nach Klasse einen anderen Rückgabewert hat. (Mysql/Access/Oracel...)
2. Da ich eigentlich prepared Statements benutzen wollte, hieße dass ja, dass ich diese in den anderen Klassen zuvor zusammenbauen muss und dann ein PreparedStatement, anstelle eines Strings zurückzugeben.
3. Ich wollte eigentlich die Exeptions über die DAO Klasse werfen und kam dadurch darauf, dass ich diese dann auch im Interface definieren muss. Gibt es bei den verschiedenen Datenbanken noch andere Exceptions als SQLException?

und

4. Wie ich von Beginn eines Projektes weiter gehen sollte, hat noch keiner beantwortet

Vielen Dank und Gruß
seejay


----------



## maki (3. Dez 2007)

1. Deine DAOs sollten keine Singletons sein, sondern die DAO Factory.
2. Was auch immer deine DAOs intern benutzen, sollte intern bleiben.
3. DAOs sollten nur und ausschliesslich DAO Exceptions werfen, klar darst du über Exceptionchaining die original Exceptions mitliefern, aber die Benutzer der DAOs sollten sich nicht mit SQL Exceptions rumschlagen müssen.
4. Kannst du diese Frage konkretisieren?

Deine DAOs sollten imho keine Resultsets liefern, sondern deine eigenen Objekte bzw. Listen davon.
Deine DAOs sollten keine Queries entgegenehmen, sondern diese erzeugen.
Wenn du Singletons einsetzt, dann doch bitte welche die Threadsafe sind, ausser du bist absolut sicher, das es keine Nebenläufigen Aufrufe gibt.


----------



## seejay (3. Dez 2007)

Ok so jetzt hab ich nochmal alles überarbteitet

```
public abstract class DAOFactory {
    
    //Liste mit allen verfügbaren Daten
    public static final int MYSQL = 1;
    /**
     * Creates a new instance of DAOFactory
     */
    protected DAOFactory() {
    }
    public abstract UserDAO getUserDAO();
    
    public static DAOFactory getDAOFactory( int database) throws DAOException 
    { 
        switch (database) 
        {
            case MYSQL: 
                return new MySQLDAOFactory();
            default: 
                return null;
        }
    }   
}
```


```
public class MySQLDAOFactory extends DAOFactory{ 

    private static final String DRIVER= "org.gjt.mm.mysql.Driver";
    private static final String DBURL=  "jdbc:mysql://localhost:3306/seejay" ;
    private static final String USER = "root";
    private static final String PW = "";
    
    public static Connection con = null;
    
    private MySQLDAOFactory instance = null;
    
    protected MySQLDAOFactory() throws DAOException
    {
        try {
            Class.forName( DRIVER );
            con = DriverManager.getConnection( DBURL, USER, PW );
        } catch (ClassNotFoundException ex) {            
            ex.printStackTrace();
            throw new DAOException("ClassNotFoundException");
        } catch (SQLException ex) {
            ex.printStackTrace();
            throw new DAOException("SQLException");            
        }
    }
    
    public MySQLDAOFactory getInstance() throws DAOException
    {
        if (instance==null)
            instance = new MySQLDAOFactory();
        return instance;
    }

    public UserDAO getUserDAO() 
    {
        // UserCustomerDAO implements CustomerDAO
        return new MySQLUserDAO();
    }
}
```


```
public interface UserDAO {
    
    public User getUser(int id) throws DAOException;
    public boolean deleteUser(int id) throws DAOException;
    public boolean updateCustomer(User upUser) throws DAOException;  
}
```


```
public class MySQLUserDAO implements UserDAO{
    
    /** Creates a new instance of MySQLUserDAO */
    public MySQLUserDAO() {
    }

    public User getUser(int id) throws DAOException{       
        PreparedStatement s;
        try {
            s = MySQLDAOFactory.con.prepareStatement("SELECT name FROM `user` where id = ?;");
            s.setInt(1,id);
            ResultSet rs = s.executeQuery();
            return new User(rs.getString("name"));
        } catch (SQLException ex) {
            ex.printStackTrace();
            throw new DAOException("SQLException");
        }
    }

    public boolean deleteUser(int id) throws DAOException {
        PreparedStatement s;
        try {
            s = MySQLDAOFactory.con.prepareStatement("Delete From `user` where id = ?;");
            s.setInt(1,id);
            ResultSet rs = s.executeQuery();
            return true;
        } catch (SQLException ex) {
            ex.printStackTrace();
            throw new DAOException("SQLException");
        }    
    }

    public boolean updateCustomer(User upUser) throws DAOException {
        PreparedStatement s;
        try {
            s = MySQLDAOFactory.con.prepareStatement("Update `user` SET name = ? where id = ?;");
            s.setInt(1,upUser.getID());
            s.setString(2,upUser.getName());
            ResultSet rs = s.executeQuery();
            return true;
        } catch (SQLException ex) {
            ex.printStackTrace();
            throw new DAOException("SQLException");
        }
    }
    
}
```


```
public class DAOException extends Exception{
    
    public DAOException(String s)
    {
        super(s);	
    }    
}
```

So ist es jetzt richtig?!? Boah is ja echt hammer...

Zu Frage 4:
Wie gehe ich am besten an ein Projekt heran. Ich habe mir aufgeschrieben, was es alles können soll. Jetzt ist die Frage wie ich am besten weiter machen soll. So dass ich einen roten Faden durch meine Projekterstellung habe. Bin bisher immer in ein Bastelmodell verfallen. Dass ich irgendwann nicht mehr wusste was wo gemacht wird etc.. Also die Planungsphase ist mir wichtig. Ums nochmal kurz zusammen zu fassen. Was sollte ich als nächstes machen 

Vielen Dank und Gruß
seejay


----------

