# Gutes Code Design (3 Schichten Modell)



## casi91 (16. Mai 2012)

Hallo Community,

ich stehe derzeit ein wenig auf dem Schlauch und hoffe ihr könnt mir weiterhelfen.
(Habe über die sufu keinen Thread gefunden, der mir hilft. Sollte einer mit ähnlicher Fragestellung vorhanden sein und ich habe ihn nicht gefunden, entschuldige ich mich an dieser Stelle)

Es geht um folgendes.
Ich bin seit längerem an einem Verwaltungsprogramm am arbeiten (Stammdatenverwaltung, Laufdaten etc). Für den Datenbankzugriff verwende ich Hibernate.

Als ich angefangen hatte, hatte ich noch äußerst wenig struktur in meinem Code. 
D.h. In meiner "View" wurden Datenmanipulationsoperationen ausgeführt und auch in die DB geschrieben.
Nach diversen Vorlesungen auf der Uni habe ich dann festgestellt, dass das äußerst unschön und auch absolut nicht zweckmäßig ist.
Also habe ich angefangen, mein Projekt in das 3-Schichten-Modell:

Ansicht
Funktionalität
Datenbank

aufzuteilen.
Das hat an sich soweit auch alles gut geklappt, es ist mit sicherheit noch nicht perfekt, aber das kommt ja vielleicht noch.

Nun zu meinem Problem:
Um die Datenbankzugriffmethoden an einer einheitlichen Stelle zu haben, habe ich mir eine Klasse "DBAktionen" erstellt in der beispielsweise die "SessionFactory" verwaltet wird.

Nach und nach kamen hier mehr und mehr Methoden zum speichern, löschen, ändern hinzu und mittlerweile ist die Klasse so groß, dass aus der einst übersichtlichen Klasse ein riesen tohuwabohu entstanden ist. (über 2000 Zeilen)
Nun spiele ich mit dem Gedanken, die Klasse in mehrere Subklassen aufzusplitten, tue mir dabei aber etwas schwer.
Wäre nett, wenn mir hier jemand einen Ratschlag geben könnte 

Kurzes Beispiel meiner Klasse "DBAktionen"

Methoden:
-speicherKunden
-aenderKunden
-loescheKunden
-sucheKundeByName

-speicherKontakt
-aenderKontakt
-loescheKontakt
-sucheKontaktByName
-sucheKontaktById

-speicherTelefonat
-aenderTelefonat
-loescheTelefonat
-ladeTelefonate

und so weiter und sofort.
Manche dieser Methoden greifen natürlich auch auf andere Methoden in dieser Klasse zu.
So wird bei einem Telefonat beispielsweise die Id des Kontaktes mitgegeben und in Telefonat wird dann "sucheKontaktById" aufgerufen um den korrekten Kontakt mit abzuspeichern.

Ich hoffe mein Problem ist einigermaßen verständlich beschrieben 
Danke schonmal im vorraus.


----------



## dev.buzz (16. Mai 2012)

Haut mich wenn ich falsch liege, soweit ich das jetzt verstanden hab brauchst du nur eine Superklasse und in diese kommen die Methoden rein die in den unteren Klassen verwendet werden.


----------



## maki (16. Mai 2012)

*verschoben*

Bei einem 3 Schicht modell sollte man einmal das DAO Pattern kennen: 
https://community.jboss.org/wiki/GenericDataAccessObjects
.. und zum anderen MVC.


----------



## Pippl (16. Mai 2012)

Was du suchst nennt sich DAO-Pattern 
Da gibt es dann für jede Modelklasse (Kunde, Kontakt, Telefonat, ...) eine eigene DAO-Klasse welche eben jene Daten speichert/liest/ändert/löscht.

EDIT: naja 2 min zu spät,  blöde kontrolle ob ich eh keinen blödsinn verzapfe


----------



## casi91 (16. Mai 2012)

erst mal Danke für die schnelle Antwort :applaus:
und @maki, sorry dass ich den Thread in der Falschen Kategorie aufgemacht habe.

Ich werd mir dann nun mal einiges über DAO-Pattern anlesen


----------



## tfa (16. Mai 2012)

Das was du suchst, heißt DAO (Data Access Object). 
Da DAOs für verschiedene Klasse durchaus recht ähnlich sind, können Generics hier hilfreich sein (Generic DAO). 
Einen Kreuz- und Quer-Zugriff verschiedene DAOs untereinander würde ich allerdings vermeiden. Dann lieber eine zusätzliche (Service-)Schicht einbauen, die die DAOs benutzt.


----------



## ARadauer (16. Mai 2012)

Im Grunde hast du dir die Antwort ja schon selber gegeben, in dem du die vier Methoden der jeweiligen Entität grupiert hast also du sie hier aufgelistest hast.... ;-) So würd ich sie in eigene Klassen zusammen packen...


----------



## casi91 (18. Mai 2012)

Guten Morgen,
nachdem ich mir jetzt schon einiges über DAO-Patter durchgelesen habe (und evtl noch nicht sehr viel schlauer geworden bin :-( ) hier mal ne Kurze Rückfrage (bzw. eine Zusammenfassung, wie ichs bisher dann verstanden habe).

(Leider bin ich aus dem Link von maki nicht viel schlauer geworden :/ vielleicht muss ich den aber einfach noch 2,3 mal lesen)

Ich habe zu jeder meiner Datenbanktabellen eine entsprechende JAVA-Klasse mit Hibernate Annotations.
(Das wären dann ja die genannten "Modellklassen" wie Pippl es ausgedrückt hat?)

Und zu jeder dieser Klassen, soll ich nun eine weitere Klasse schreiben, die die entsprechenden Methoden wie speicherKunden, aenderKunden, loescheKunden, sucheKundeByName, etc... beinhaltet und dann die entsprechende Hibernateklasse zur DB-Kommunikation benutzt?

Und wenn ich das richtig verstanden habe, soll ich bei "kreuz und quer"-Abfragen eine weitere Klasse schreiben, die die verschiedenen DAO's einbindet und dann die entsprechende Abfrage implementiert?
(Beispielsweise: sucheTelefonateMitKontakt)

Und dann gibt es ja natürlich Methoden, die gleich sind (beispielsweise CRUD-Methoden)
Deshalb sollte ich dann an gegebener Stelle Generics verwenden?

Könnte mir evtl jemand ein kleines Modellhaftes beispiel geben, wie ich sowas aufbauen würde?
Steh da grad ziemlich auf dem Schlauch -.-"
(sorry, wenn die ein oder andere Frage/Anmerkung ein wenig Dumm ist...)


----------



## Pippl (18. Mai 2012)

casi91 hat gesagt.:


> anktabellen eine entsprechende JAVA-Klasse mit Hibernate Annotations.
> (Das wären dann ja die genannten "Modellklassen" wie Pippl es ausgedrückt hat?)
> 
> Und zu jeder dieser Klassen, soll ich nun eine weitere Klasse schreiben, die die entsprechenden Methoden wie speicherKunden, aenderKunden, loescheKunden, sucheKundeByName, etc... beinhaltet und dann die entsprechende Hibernateklasse zur DB-Kommunikation benutzt?



Genau



casi91 hat gesagt.:


> Und wenn ich das richtig verstanden habe, soll ich bei "kreuz und quer"-Abfragen eine weitere Klasse schreiben, die die verschiedenen DAO's einbindet und dann die entsprechende Abfrage implementiert?
> (Beispielsweise: sucheTelefonateMitKontakt)



Richtig, damit in der KundeDAO Klasse nur Methoden drinnen sind welche mit der Kunden Tabelle arbeiten usw.



casi91 hat gesagt.:


> Und dann gibt es ja natürlich Methoden, die gleich sind (beispielsweise CRUD-Methoden)
> Deshalb sollte ich dann an gegebener Stelle Generics verwenden?



Genau als Basis ein Interfaces oder eine Abstrakte Klasse mit Generics.

Hier ein Beispiel:
[URL="http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html]DAO Pattern Beispiel (ohne Generics)[/URL]


----------



## casi91 (21. Mai 2012)

Guten Morgen allerseits 
Vielen Dank für die Antworten.
Hab es jetzt an dem Beispiel von maki: https://community.jboss.org/wiki/Gen...aAccessObjects
und an diesem: DAO pattern for Hibernate!  Five ‘s Weblog
aufgebaut.
Langsam aber sicher verstehe ich, wie das ganze laufen sollte 
Nur noch eine Frage:
Ist es absolut unschön in eine DAO, das beispielsweise mit der Standort-Tabelle arbeitet, ein anderes DAO einzubinden?
Ich bin mir das nur am überlegen, da ich teilweise für einen 10 Zeiler dann eine neue Klasse erstellen würde.
Beispielsweise habe ich eine Methode, um einen neuen Standort anzulegen.
Hierbei wird der Kreis, das Land und das Bundesland mit namen mitgegeben. 
Dann wird geprüft, ob die mitgegebenen Informationen schon existieren.
Sprich:

```
kreis = factory.getKreisDAO.searchByName(name)
```
usw.

Derzeit hab ich das in meinem "Standort" DAO drinnen.
So wie ich es bisher verstanden habe, wäre es ja aber schöner eine neue DAO zu machen, die dann von mir aus "AenderStandortInformationenDAO" heißen würde (oder so ähnlich)

Ich muss sagen, dass ich mir derzeit nicht sicher bin, ob das wirklich der Übersichtlichkeit dienlich ist.
Lass mich aber gerne vom Gegenteil überzeugen


----------



## Pippl (21. Mai 2012)

casi91 hat gesagt.:


> Nur noch eine Frage:


Wird sicher nicht die letzte sein 



casi91 hat gesagt.:


> Ist es absolut unschön in eine DAO, das beispielsweise mit der Standort-Tabelle arbeitet, ein anderes DAO einzubinden?
> Ich bin mir das nur am überlegen, da ich teilweise für einen 10 Zeiler dann eine neue Klasse erstellen würde.
> Beispielsweise habe ich eine Methode, um einen neuen Standort anzulegen.
> Hierbei wird der Kreis, das Land und das Bundesland mit namen mitgegeben.
> ...



Nein so eine Klasse ("AenderStandortInformationenDAO") wäre nicht wirklich die Lösung.
1. Ist die Wahl des Klassennamen unglücklich weil er ein Verb enthält.
2. Gibt es ja schon ein StandortDAO und mit dieser neuen Klassen hättest du ein weiteres was nur zum Ändern von (?) gut wäre

Aber du hast die Lösung ja irgendwie schon, du überprüfst ob die Informationen schon existieren. 
Sprich du hast eine Klasse mit einer Methode welche die eingegeben Daten bekommt, dann rufst du mal das KreisDao auf und prüfst ob der Kreis schon vorhanden ist, danach das Land und dann das Bundesland. Sollte eines davon nicht existieren und aber gebraucht werden, so müsste man einen Fehler werfen und diesen entsprechend behandeln (User muss vorher das Bundesland anlegen zum Beispiel).


```
public void validateStandortInfos(String kreis, String land, String bundesland)
{
    Kreis kreis = factory.getKreisDAO().searchByName(kreis);
    if(kreis==null)
    {
        throw new Exception("Angegebener Kreis, nicht vorhanden muss vorher angelegt werden");
    }
    Land land = factory.getLandDAO().searchByName(land);
    if(land==null)
    {
        // hier könnte man auch statt eine fehlermeldung zu werfen auch ein land anlegen, falls die daten dazu ausreichen
    }
    ...
}
```

Das wäre mal grob auf die schnelle ein Beispiel, die anderen können sicher noch a bissl was daran verbessern =P


----------



## darekkay (21. Mai 2012)

Du hast richtig erkannt, dass jedes DAO nur auf seine eigene Tabelle zugreifen sollte. Es hält dich aber nicht davon ab, ein anderes DAO aufzurufen, das auf einer ganz anderen Tabelle arbeitet.

Wenn ich es richtig verstanden habe, benötigst du StandortDAO und KreisDAO. Du kannst von StandortDAO aus KreisDAO nutzen. Jede Klasse greift dabei nur auf eine Tabelle zu, der Standort hängt aber nun mal vom Kreis ab. Dein Code (bzw. der von Pippl) ist also in Ordnung. Allerdings wirst du Probleme haben, wenn du dein StandortDAO testen möchtest. Denn du kannst diese nicht unabhängig von den anderen DAOs untersuchen, da diese direkt im Code benutzt werden (bzw. über die Factory geholt werden). Die Lösung hierfür nennt sich Dependency Injection:

```
private final KreisDAO kreisDAO;

public StandortDAO(KreisDAO kreisDAO){
this.kreisDAO = kreisDAO
}

public void createStandort(Standort standort){

if(kreisDAO.exists(...))

}
```


----------



## Pippl (21. Mai 2012)

darekkay hat gesagt.:


> Dein Code (bzw. der von Pippl) ist also in Ordnung. Allerdings wirst du Probleme haben, wenn du dein StandortDAO testen möchtest.



Ich sehe bei meinem Code kein Problem mit dem Testen, da diese Methode in einer Business Klasse steht und nicht in der StandortDAO Klasse!
Somit kann das DAO ganz normal getestet werden (Standort erstellen, bearbeiten, löschen etc).
Bei deinem jedoch muss man beim Testen zusätzlich zum StandortDAO noch ein KreisDAO erstellen. Oder denke ich da falsch????:L


----------



## darekkay (21. Mai 2012)

Ich bin gerade dabei, mich in DI einzuarbeiten, und an dem Beispiel wird das aber (für mich) recht deutlich. Am besten erstmal die Einstiegslektüre:
Dependency Injection ? Wikipedia
Inversion of Control ? Wikipedia

Auf dein Beispiel bezogen: du kannst StandortDAO nicht völlig unabhängig testen. Und genau das ist ja der Sinn eines UnitTests. In dem Code rufst du nämlich die Factories auf. Schreibst du also einen Test für StandortDAO, musst du den Factory-Code direkt nutzen, den du implementiert hast. Du kannst auch nicht einfach eine MockFactory überreichen. In meinem Code hingegen muss ich überhaupt nicht wissen, was KreisDAO macht. Ich kann ein MockKreisDAO erstellen, das mir bsp. immer "true" liefert, und kann so völlig unabhängig davon mein StandortDAO testen.

EDIT: am Anfang des Videos wird die allgemeine Problematik richtig gut erklärt: Guice


----------



## casi91 (21. Mai 2012)

Danke euch beiden


> Wird sicher nicht die letzte sein


Da hast du wahrscheinlich Recht 
(wäre auch seltsam, wenn es die letzte sein würde ;-)


Ich hatte das ganze dann ein wenig falsch verstanden.
Ich hatte es so verstanden, dass man in einem DAO das mit der Tabelle Standort arbeitet, nicht ein anderes DAO (beispielsweise KreisDAO) einbinden sollte.
Aber wenn es sich "nur" darauf bezieht, dass man in dem DAO nicht mit 2 oder mehr Tabelle arbeiten, sondern dann die anderen DAO's einbinden sollte, dann hab ich ja bisher alles richtig gemacht :toll:

Das Thema Dependency Injection werde ich mir mal zu gemühte führen.

Es gibt doch immerwieder neue Begriffe und Techniken, an denen ich merke, wie weit mein Wissensstand eigentlich erst ist :-D


----------



## Pippl (21. Mai 2012)

[WR]Wie schon gesagt meine Methode validateStandortInfos steht nicht in der DAO Klasse von Standort, sondern in einer Business Klasse.[/WR] 

Daher wird in der StandortDAO Klasse keine Factory verwendet und auch kein anderes DAO. Und man kann das DAO ohne Probleme testen (Standort anlegen, löschen bearbeiten etc).
Soll natürlich nicht heißen das deine Möglichkeit mit DI schlecht ist oä


----------



## tfa (21. Mai 2012)

Pippl hat gesagt.:


> [WR]Wie schon gesagt meine Methode validateStandortInfos steht nicht in der DAO Klasse von Standort, sondern in einer Business Klasse.[/WR]
> 
> Daher wird in der StandortDAO Klasse keine Factory verwendet. Und man kann das DAO ohne Probleme testen (Standort anlegen, löschen bearbeiten etc).



Aber die Business-Klasse musst du doch auch testen. Und dann ist es sicherlich praktischer, wenn die Abhängigkeit zu den DAOs hinein injiziert wird, statt dass sich die Klasse sie aus einer globalen Factory holt.


----------



## darekkay (21. Mai 2012)

Ok, hab das tatsächlich übersehen. Dann ist es aber wahrscheinlich ein Service Locator(?). Gut, es ist unabhängig testbar, aber die Nachteile werden in dem Video schnell erklärt (wenn ich einen Test anlege, weiß ich z.B. nicht, was meine Factory alles bereitstellt und ich deswegen für ein Mock überschreiben muss)


----------



## Pippl (21. Mai 2012)

tfa hat gesagt.:


> Aber die Business-Klasse musst du doch auch testen. Und dann ist es sicherlich praktischer, wenn die Abhängigkeit zu den DAOs hinein injiziert wird, statt dass sich die Klasse sie aus einer globalen Factory holt.



Jap, aber wenn ich diese Business-Klasse testen will brauch ich so oder so beide DAOs, welche wiederum im Konstruktor gesetzt werden können oder eben per Factory geholt werden.


----------



## tfa (21. Mai 2012)

> Jap, aber wenn ich diese Business-Klasse testen will brauch ich so oder so beide DAOs


Hoffentlich nicht (für Unit-Tests).


----------

