DAO und weiter?

bauerb

Neues Mitglied
Hi!

Ich greife auf meine DB mittel DAO zu. Ist ja eine feine Sache und funktioniert auch gut, aber eine DAO handelt (bei mir zumindest) genau eine Tabelle.
Jetzt braucht man aber Objekte, wo mehrere Tabellen miteinander verknüpft sind.

Bsp:
* Tabelle Personen
* Tabelle Telefonnummern
* Jede Person hat mehrere Telefonnummern.

Wie macht man das am besten in ordentlichen OO-Stil?

MfG bauerb
 
M

Marcinek

Gast
Nutze dann einfach Views für komplexe Abfragen, dann brauchst du dein Framework nicht zu ändern.
 
M

maki

Gast
Wie macht man das am besten in ordentlichen OO-Stil?
Nun, du wirst ja wohl kaum Addressen abfragen ohne die zugehörige Person, brauchst also kein DAO für Addressen.

Eric Evan hat in "Domain Driven Design" schön erklärt, wie man sich sog. "Aggregate Roots" anlegt, pro Aggregate Root ein DAO (bzw. Repository).
D.h. zB. dein Personen DAO liest u.a. die Addressen und Telefonnummern mit aus, und du greifst nur über Person darauf zu.

Das hat noch nix mit ORM zu tun, und Views sind halt dann doch wieder sehr RDBMS lastig, braucht man beides nicht, wobei ORMs natürlich helfen, wenn man das nötige Verständnis von RDBMS und OO Mapping darauf mitbringt.
 

oopexpert

Mitglied
Ich zeige mal exemplarisch aus einem meiner Tutorials eine Implementation eines Fachobjekts. Es handelt sich um den Prototyp einer Projektmanagement-Software. PMSystem hält n Projekte, Methoden für das Hinzufügen und Ändern von Projekten.

Java:
public class PMSystemImpl extends PersistentObject implements PMSystem {


	private PMSystemImpl() {
		this.setId(-1);
	}


	@Override
	public Project addProject(final String name) throws DoubleProjectException, FieldException {
		
		if (name.length() == 0) throw new FieldException("Name darf nicht leer sein.");
		
		PMSystemDAO pmSystemDAO = SingletonDAOFabrik.getDAOFabrik().getPMSystemDAO();
		ProjectDAO  projectDAO  = SingletonDAOFabrik.getDAOFabrik().getProjectDAO();
		projectDAO.lock();
		
		boolean exists = projectDAO.exists(name);
		if (exists) {
			throw new DoubleProjectException(name);
		} else {
			ProjectImpl project = new ProjectImpl(name);
			pmSystemDAO.addProject(project);
			return project;
		}
	}


	@Override
	public Project renameProject(final Project oldProject, final String name) throws DoubleProjectException, FieldException {
		
		
		if (name.length() == 0) throw new FieldException("Name darf nicht leer sein.");
		
		PMSystemDAO pmSystemDAO = SingletonDAOFabrik.getDAOFabrik().getPMSystemDAO();
		ProjectDAO  projectDAO  = SingletonDAOFabrik.getDAOFabrik().getProjectDAO();
		projectDAO.lock();
		projectDAO.checkChange(oldProject);

		boolean exists = projectDAO.exists(name);
		if (exists) {
			throw new DoubleProjectException(name);
		} else {
			Project renamedProject = pmSystemDAO.renameProject((ProjectImpl) oldProject, name);
			return renamedProject;
		}
	}


	@Override
	public List<Project> addProjects(String[] names) throws DoubleProjectException, FieldException {
		List<Project> result = new ArrayList<Project>();
		
		for (String name: names) {
			result.add(this.addProject(name));
		}
		
		return result;
	}

}
 
G

Gelöschtes Mitglied 5909

Gast
@oopexpert bitte schreibe keine tutorials, sondern lies dir welche durch
 

ARadauer

Top Contributor
SingletonDAOFabrik wird nicht wirklich testbar sein...
dein PMSystemImpl ist ein PersistentObject? Stellt ein PersistentObject sowas wie eine Entität dar und erweitert deine Service Schicht das?

Aber grundstätzlich mach ich das auch oft so, dass ich über Services auf verschiedene DAOs zugreife...
 
M

maki

Gast
Auf wir nach einem flüchtigen Blick ins Auge springt:

- Der Konstruktor schaut imho nicht so dolle aus [c]this.setId(-1);[/c]

- Ich hoffe DoubleProjectException, FieldException etc. sind wenigstens RuntimeExceptions, trzotzdem sollte man sich imer mit eigenen Exceptions zurückhalten.

- [c]if (name.length() == 0) throw new FieldException("Name darf nicht leer sein.");[/c] prüft nicht auf null und könnte wohl eine NPE verursachen.

- addProject: Nun wird es schon ernster...
- * Singletons, naja, nicht dein ernst, oder?
- * ProjectDAO findet projekte, PMSystemDAO legt welche an???

- renameProject, denke dass das ein versehen war
- * projekt ist eigentlich nur ein String, etwas "flach" oder?
- * renameProject ist eine Instanzmethode, nimmt dann eine alte Instanz entgegen... denke da ist was schiefgelaufen und es handelt sich um ein versehen ;) Oder erzeugst du eine Project Instanz nur um eine andere umbenennen zu können??? Die neue instanz wird dann auhc wieder zurückgegeben... häh???
- * Interfaces sind was tolles, warum dann wieder auf die Impl casten? Macht doch die Vorteile wieder zunichte...

- addProjects nimmt ein Stringarray entgegen.. warum nicht konsequent auf ein OO Domänenmodell setzen?

Macht irgendwie nicht so viel Sinn, vor allem im Bezug auf OO, Architektur etc.
Auch rufst du eine lock Methode auf, aber nie eine unlock Methode.
Wie ARadauer schon sagte, ist PMSystemImpl nun eine Entity oder nicht?

Bitte nicht persönlich nehmen :)
 
Zuletzt bearbeitet von einem Moderator:

tfa

Top Contributor
dein PMSystemImpl ist ein PersistentObject? Stellt ein PersistentObject sowas wie eine Entität dar und erweitert deine Service Schicht das?
Genau das hab ich mich auch gefragt. Sieht irgendwie aus wie ein Mittelding aus Business-Objekt und Service, der DAOs benutzt. Ich vermisse das Value-Objekt (oder meintwegen DTO).

Eine kurze Übersicht zum DAO-Pattern findet man übrigens hier:
Core J2EE Patterns: Data Access Object
 

oopexpert

Mitglied
SingletonDAOFabrik wird nicht wirklich testbar sein
Das ist das abstrakte Fabrik-Muster. Wenn man DAO-Stubs benötigt, dann hinterlegt man bei "getDAOFabrik()" eine Fabrik, die Stub-DAOs erzeugt. Damit ist Testbarkeit in jedem Fall gegeben. Man könnte sich dahinter auch eine Spring-Konfiguration vorstellen, aber das sieht man hier natürlich nicht, weil davon abstrahiert werden soll, wie DAOs erzeugt werden.

PMSystem ist die nach außen sichtbare Schnittstelle, wenn man SingletonPMSystem.getInstance() aufruft. Damit erhalten Aufrufer einen zentralen Einstieg und programmieren nur noch gegen Interfaces. PMSystemImpl ist die Implementation des Fachobjekts und bei mir sind alle Fachobjekte ersteinmal persistent. Da es im Moment nur genau ein PMSystem gibt, habe ich in diesem Fall keine Repräsentation auf der DB, was man sich durchaus vorstellen könnten --> Mandantenfähigkeit. Deshalb im Konstruktor die definierte -1.

Ich hoffe DoubleProjectException, FieldException etc. sind wenigstens RuntimeExceptions, trzotzdem sollte man sich imer mit eigenen Exceptions zurückhalten

Nein, es sind keine RuntimeExceptions. meine Absicht ist es, den Aufrufer über die möglichen Fehler explizit zu informieren, die in der Schicht der Fachobjekte auftreten können. Es sind ausschließlich Exceptions der aktuellen Schicht, nicht der darunterliegenden Schichten (DAO, JDBC, DB), die nicht als RuntimeException durchschlagen.

if (name.length() == 0) throw new FieldException("Name darf nicht leer sein");
Ich prüfe nie auf null. Das ist eine Alibi-Prüfung. Einzig und allein würde ich ein assert machen. Die Regel, die ich verinnerlicht habe ist: Übergebe nie null, und gebe nie null zurück. Sollten externe APIs diese Regel verletzenm werden diese gekapselt, damit man sich nicht mehr um solche Null-Prüfungen kümmern muss. Es gibt 2 Ausnahmen für die null-Verwendung: Abschluss rekursiver Datenstrukturen am Root und lazy initialization. In allen anderen Situationen machen die Null-Prüfungen erstmal keinen Sinn, wenn es nicht gute Gründe dafür gibt.

Singletons, naja, nicht dein ernst, oder?
Doch. Für mich sind semantische Singletons zentrale Einstige in Schichten und sind für mich Fachobjekte mit Verantwortlichkeiten. Des Weiteren muss ich mich nicht um die Erzeugung eines "konkreten" PMSystemImpl kümmern, sondern erhalte ein Pbjekt mit dem Interface PMSystem zurück. Die Erzeugung bleibt mir verborgen.

ProjectDAO findet projekte, PMSystemDAO legt welche an???
Korrekt: An der Stelle bin ich tatsächlich ein bischen unsauber, wobei man da nur über die Verlagerung von Methoden redet.


renameProject, denke dass das ein versehen war
Nein.

projekt ist eigentlich nur ein String, etwas "flach" oder?
Das ich dort einen String übergebe, hängt mit der Verantwortlichkeit des PMSystem zusammen. Ein PMSystem ist für die Erzeugung von Project-Instanzen verantwortlich, kein anderer. Wenn mehr Parameter übergeben werden sollen, dann bietet sich ein sogenanntes Value-Object an, welches die Informationen eines VORSCHLAGS für ein Project-Objekt beinhaltet.


renameProject ist eine Instanzmethode, nimmt dann eine alte Instanz entgegen... denke da ist was schiefgelaufen und es handelt sich um ein versehen Oder
Nein, es ist nichts schiefgelaufen: Meine Fachobjekte sind Immutable, was übrigends auch anzustreben ist, glaubt man Joshua Bloch (Effective JAVA). Deshalb wird eine neue Instanz erzeugt, die das Project als "neue Version" beinhaltet. In der "projectDAO.checkChange(oldProject...)" findet die Versionsprüfung statt, welche eine RuntimeException wirft: OptimisticLockException. SOllte man auf einem veralteten Objekt weiterarbeiten wollen, insbesondere eine Namensänderung vornehmen, funktioniert das natürlich nicht. (Lost-Update-Problematik)


Interfaces sind was tolles, warum dann wieder auf die Impl casten? Macht doch die Vorteile wieder zunichte.
Grundsätzlich korrekt ABER: Es gibt PMSystemImpl und es gibt ProjectImpl und so weiter. Objekte dieses Typs, sind innerhalb einer Schicht und dürfen sich natürlich in ihrer konkreten Ausprägung kennen. Deshalb ist der Cast unproblematisch. Für den Aufrufer über SingletonPMSystem sind nur die Interfaces relevant: PMSystem, Project. Der "Nutzer" dieser API sollte natürlich keine Casts durchführen, aber der Entwickler der Business-API darf dies, weil er die Schicht kennt und die Klassen zusammenentwickelt. Deshalb gilt diese Aussage natürlich nur für Objekte von Klassen mit hoher semantischer Kohäsion.


addProjects nimmt ein Stringarray entgegen.. warum nicht konsequent auf ein OO Domänenmodell setzen?
Siehe Aufruf der Method addproject(String name). Es handelt sich hierbei nur eine Erweiterung der einfachen Methode, die die Anlage mehrerer Projekte vornimmt, auch wenn sie fachlich gesehen wahrscheinlich nicht allen Anforderungen standhält, wie z.B. MultiFehlermeldungen. Und nochmal: Ich sehe das PMSystem in der Verantwortung, Projekte zu erzeugen, und nicht den externen Aufrufer.

Auch rufst du eine lock Methode auf, aber nie eine unlock Methode.
der Lock wird am Ende der Transaktion auf der DB wieder gelöst, entweder über ein Rollback oder ein Commit. Das Transaktionshandling ist für den fachlichen Quellcode transparent über einen Aspekt realisiert, der um die Methoden der Fachobjekte gewebt wurde. Die Konventionen zum Commit und zum Rollback sind einfach: Exception --> Rollback, method-Leave --> Commit.

Wie ARadauer schon sagte, ist PMSystemImpl nun eine Entity oder nicht?
Es soll sich wie eine Entity verhalten, hat aber in der Ausbaustufe keine Tabellenrepräsentation. Darüber würde ich sprechen, wenn es um Mandantenfähigkeit geht...

Ich entschuldige mich dafür, dass ich so weit aushole und das Thema des Fragestellers verfehle mit diesem Beitrag. Aber ich sah es als notwendig an, meine Design-Entscheidungen zu rechtfertigen. Meine Vorstellung war es mit dem Code-Snipplet lediglich darzustellen, wie ich mit DAOs innerhalb meiner Fachobjekte umgehe, mehr nicht. Ich sehe dass so, dass jeder andere Beitrag aus einer anderen Vorstellung resultierte, was OO ist.
 
M

maki

Gast
Das ist das abstrakte Fabrik-Muster. Wenn man DAO-Stubs benötigt, dann hinterlegt man bei "getDAOFabrik()" eine Fabrik, die Stub-DAOs erzeugt. Damit ist Testbarkeit in jedem Fall gegeben. Man könnte sich dahinter auch eine Spring-Konfiguration vorstellen, aber das sieht man hier natürlich nicht, weil davon abstrahiert werden soll, wie DAOs erzeugt werden.
Schon klar was das ist ;)
Aber warum Resourcelookup in Form eines Singletons nutzen, um dann später DI (Spring) nachzuziehen?
ResourceLookup ist das Gegenteil von DI, man hat keine Vorteile wenn man beides gleichzeitig einsetzt.

PMSystem ist die nach außen sichtbare Schnittstelle, wenn man SingletonPMSystem.getInstance() aufruft. Damit erhalten Aufrufer einen zentralen Einstieg und programmieren nur noch gegen Interfaces. PMSystemImpl ist die Implementation des Fachobjekts und bei mir sind alle Fachobjekte ersteinmal persistent. Da es im Moment nur genau ein PMSystem gibt, habe ich in diesem Fall keine Repräsentation auf der DB, was man sich durchaus vorstellen könnten --> Mandantenfähigkeit. Deshalb im Konstruktor die definierte -1.
Auch hier widersprichst du dir in deiner Argumentation:
Es ist Persistent, aber nicht in der DB...

Nein, es sind keine RuntimeExceptions. meine Absicht ist es, den Aufrufer über die möglichen Fehler explizit zu informieren, die in der Schicht der Fachobjekte auftreten können. Es sind ausschließlich Exceptions der aktuellen Schicht, nicht der darunterliegenden Schichten (DAO, JDBC, DB), die nicht als RuntimeException durchschlagen.
Naja, checked vs. unchecked Exceptions ist schon oft genug durchdiskutiert worden, das Ergebnis war immer: checked Exceptions waren ein Designfehler in der Java API, haben mehr Nachteile als Vorteile.

Ich prüfe nie auf null. Das ist eine Alibi-Prüfung. Einzig und allein würde ich ein assert machen. Die Regel, die ich verinnerlicht habe ist: Übergebe nie null, und gebe nie null zurück. Sollten externe APIs diese Regel verletzenm werden diese gekapselt, damit man sich nicht mehr um solche Null-Prüfungen kümmern muss. Es gibt 2 Ausnahmen für die null-Verwendung: Abschluss rekursiver Datenstrukturen am Root und lazy initialization. In allen anderen Situationen machen die Null-Prüfungen erstmal keinen Sinn, wenn es nicht gute Gründe dafür gibt.
Gut, wenn kein null möglich sein darf, sollte man auch nict prüfen, falls es doch zu einer NPE kommt, ist es ein Programmierfehler.

Doch. Für mich sind semantische Singletons zentrale Einstige in Schichten und sind für mich Fachobjekte mit Verantwortlichkeiten. Des Weiteren muss ich mich nicht um die Erzeugung eines "konkreten" PMSystemImpl kümmern, sondern erhalte ein Pbjekt mit dem Interface PMSystem zurück. Die Erzeugung bleibt mir verborgen.
Du nutzt aber nicht nur semantische Singletons wie man sie mit DI bekommt, du nutzt wirklich fest verdrahtete Singletons mit static aufruf & Kopplung.

Das ich dort einen String übergebe, hängt mit der Verantwortlichkeit des PMSystem zusammen. Ein PMSystem ist für die Erzeugung von Project-Instanzen verantwortlich, kein anderer. Wenn mehr Parameter übergeben werden sollen, dann bietet sich ein sogenanntes Value-Object an, welches die Informationen eines VORSCHLAGS für ein Project-Objekt beinhaltet.
Naja, wenn PMSystem dafür verantwortlich ist, und der Rest des System dann nicht mehr mit Strings arbeitet sondern mit Project Objekten, ist das auch ok.

Nein, es ist nichts schiefgelaufen: Meine Fachobjekte sind Immutable, was übrigends auch anzustreben ist, glaubt man Joshua Bloch (Effective JAVA). Deshalb wird eine neue Instanz erzeugt, die das Project als "neue Version" beinhaltet. In der "projectDAO.checkChange(oldProject...)" findet die Versionsprüfung statt, welche eine RuntimeException wirft: OptimisticLockException. SOllte man auf einem veralteten Objekt weiterarbeiten wollen, insbesondere eine Namensänderung vornehmen, funktioniert das natürlich nicht. (Lost-Update-Problematik)
Da muss man jetzt genauer hinsehen was wirklich gemeint ist.
Blochs Tipp bezieht sich auf sog. ValueObjects (siehe definition bei Eric Evans), diese sind genau das Gegenteil von Entitäten, werden durch ihren Wert definiert, nicht durch eine eigene ID.
Entitäten immutable zu machen ist sicherlich nicht empfehlenswert, bei jeder Änderung müsstest du eine Entity erst löschen und dann neuspeichern...
Project ist aber wohl nur ein ValueObject und keine Entität, und so sieht es auch aus, schliesslich suchst du nach Project per name Attribut, also einem Wert.
Trotzdem wirst du nicht umhin kommen, auch Entitäten zu verwenden, und diese kann man eben nicht Immutable machen ;)

Grundsätzlich korrekt ABER: Es gibt PMSystemImpl und es gibt ProjectImpl und so weiter. Objekte dieses Typs, sind innerhalb einer Schicht und dürfen sich natürlich in ihrer konkreten Ausprägung kennen. Deshalb ist der Cast unproblematisch. Für den Aufrufer über SingletonPMSystem sind nur die Interfaces relevant: PMSystem, Project. Der "Nutzer" dieser API sollte natürlich keine Casts durchführen, aber der Entwickler der Business-API darf dies, weil er die Schicht kennt und die Klassen zusammenentwickelt. Deshalb gilt diese Aussage natürlich nur für Objekte von Klassen mit hoher semantischer Kohäsion.
Wieso ist es natürlich dass sich Objekte in derselben Schicht in der konkreten Ausprägung kennen?
Factories zB. würden dieses Problem übrigens lösen ;)

Siehe Aufruf der Method addproject(String name). Es handelt sich hierbei nur eine Erweiterung der einfachen Methode, die die Anlage mehrerer Projekte vornimmt, auch wenn sie fachlich gesehen wahrscheinlich nicht allen Anforderungen standhält, wie z.B. MultiFehlermeldungen. Und nochmal: Ich sehe das PMSystem in der Verantwortung, Projekte zu erzeugen, und nicht den externen Aufrufer.
Das verstehe ich.

der Lock wird am Ende der Transaktion auf der DB wieder gelöst, entweder über ein Rollback oder ein Commit. Das Transaktionshandling ist für den fachlichen Quellcode transparent über einen Aspekt realisiert, der um die Methoden der Fachobjekte gewebt wurde. Die Konventionen zum Commit und zum Rollback sind einfach: Exception --> Rollback, method-Leave --> Commit.
Transactionsmanagament sollte man auch am besten deklerativ machen, das meinte ich nciht.
Was ich aber meine ist: Du rufst explizit eine lock Methode auf, aber nirgendwo rufst du explizit eine unlock o.ä. Methode auf, da fehlt imho symetrie.

Es soll sich wie eine Entity verhalten, hat aber in der Ausbaustufe keine Tabellenrepräsentation. Darüber würde ich sprechen, wenn es um Mandantenfähigkeit geht...
Sorry, aber der Code sagt es ist eine Entity, aber du verwendest sie anders.. merkst du etwas?
Hat auch nix mit anderen Aspekten wie Mandantenfähigkeit zu tun.
"Ich bin eine Ente, aber ich quake nicht, ich habe keinen Schnabel, keine federn, keine Flügel... ich bin eine Ente?!"

Ich entschuldige mich dafür, dass ich so weit aushole und das Thema des Fragestellers verfehle mit diesem Beitrag. Aber ich sah es als notwendig an, meine Design-Entscheidungen zu rechtfertigen. Meine Vorstellung war es mit dem Code-Snipplet lediglich darzustellen, wie ich mit DAOs innerhalb meiner Fachobjekte umgehe, mehr nicht. Ich sehe dass so, dass jeder andere Beitrag aus einer anderen Vorstellung resultierte, was OO ist.
Naja, das sehe ich anders.
Ohne dir zu nahe treten zu wollen, ichwürde ich das nicht als Beispiel verwenden.
 

oopexpert

Mitglied
Aber warum Resourcelookup in Form eines Singletons nutzen, um dann später DI (Spring) nachzuziehen?
Ich schlage 2 Fliegen mit einer Klappe:
  • Pro DAOFactory habe ich nur eine Spring-Bean (je weniger XML-Konfiguration, desto besser, Stichwort Medienbruch)
  • Die DAOs sind Methoden-lokal UND ich muss keine setter deklarieren (SETTER INJECTION) UND ich brauche keinen speziellen Konstruktor (CONSTRUCTOR INJECTION) UND ich brauche keine speziellen Parameter (PARAMETER INJECTION)
Ich habe etwas weniger Granularität, weil ich DAOs einer Factory gemeinsam betrachte, aber das ist auch in dem Sinne der abstrakten Fabrik.

Auch hier widersprichst du dir in deiner Argumentation:
Es ist Persistent, aber nicht in der DB...
Ich sage, es soll sich wie ein PersistentObject verhalten, mehr nicht, Stichwort Polymorphie. Die Abbildung auf eine DB-Tabelle ist transparent für jeden Aufrufer.

Du nutzt aber nicht nur semantische Singletons wie man sie mit DI bekommt, du nutzt wirklich fest verdrahtete Singletons mit static aufruf & Kopplung.
Die Aufrufe sind so lokal wie möglich (Stichwort Sichtbarkeit) , denn nur die Methode kennt die DAOs, die sie selber benötigt. Des Weiteren ist die Kopplung lose, weil PMSystemDAO und ProjectDAO Interfaces sind. Ach ja: DAOFabrik ist auch ein Interface. SingletonDAOFabrik ist eine Hilfsklasse, die selber keine DAOFabrik ist, sondern sich eine Singleton-DAOFabrik holt. Die Inderektion ist der losen Kopplung geschuldet.

Wieso ist es natürlich dass sich Objekte in derselben Schicht in der konkreten Ausprägung kennen
Kohäsion vs. Lose Kopplung.

Project ist aber wohl nur ein ValueObject und keine Entität, und so sieht es auch aus, schliesslich suchst du nach Project per name Attribut
Ich bin mir im Moment nicht sicher, ob wir dasselbe unter Objekt, Entität, Value Object oder DTO verstehen.
Tatsache ist: Fachlich soll der Name eindeutig sein. Technisch kriegt das Objekt natürlich irgendeine DB-Id verpasst.

Ein DTO ist es definitiv nicht. Ein ValueObject ist aus meiner Sicht nur ein DatenContainer, der wenig bis keine interne Logik hat und das Prinzip der Kapselung minimiert. Eine Entität ist irgendetwas, was eine Identität und einen Typ hat. und ein Objekt ist für mich eine Entität mit Logik, welche das Prinzip der Kapselung maximiert. Ich spreche wahrscheinlich von Objekten.

Du rufst explizit eine lock Methode auf, aber nirgendwo rufst du explizit eine unlock o.ä. Methode auf, da fehlt imho symetrie.
Der Lock reichert die Methode um Multi-User-Behandlung an. Die Asymmetrie ist zweifelsfrei gegeben. Aktuell sehe ich aber keine andere Möglichkeit, weil ich nicht weiss, woher Fachmethoden später überall her aufgerufen werden und der Aufrufer die Methode in seinem ganz eigenen größeren transaktionalen Kontext aufrufen möchte. Deshalb erfolgt der De-Lock immer am Ende der Transaktion implizit und nicht explizit.
 
Zuletzt bearbeitet:
G

Gelöschtes Mitglied 5909

Gast
abgesehen von dem was schon geschrieben wurde, wundert es mich dass sich noch keiner Über die Namensgebung beschwert hat

nimms nicht persönlich, aber wenn ich sowas sehe muss ich kotzen

- du verwendest englisch und deutsch und vermischt die sogar in einem namen
- du verwendest abkürzungen
- du verwendest namen wie "System" was sehr allgemein ist

- das was du als PMSystem bezeichnest würde ich beispielsweise ProjectService nennen,
von einem System (oder was ich darunter verstehe) ist das ding weit entfernt
- du schreibst extra eine DoubleProjectException, die genau 1 mal verwendet wird!?!?
- die message ist dann natürlich wieder auf deutsch...
 

oopexpert

Mitglied
Die Frage war "DAO und Weiter?".

Mal davon abgesehen, dass...
... es hier nicht um Internationalisierung/Lokalisierung geht, ...
... es nicht um Namenskonventionen geht, ...
habe ich eine durchgängige englische Benennung meiner eigenen Artefakte.

Wem bin ich eigentlich aufn Schlips getreten? ???:L

Das Problem ist, dass die Klasse völlig aus dem Kontext der Anwendung und auch dem Konzept gerissen ist und sich damit natürlich viele Fragen stellen. Mir wäre ein etwas pragmatischere Sichtweise auf den geposteten Quellcode, bezogen auf die Fragestellung, wichtig. Ich kann ja nicht die gesamte Anwendung hier posten.
 
M

maki

Gast
Ich schlage 2 Fliegen mit einer Klappe:
  • Pro DAOFactory habe ich nur eine Spring-Bean (je weniger XML-Konfiguration, desto besser, Stichwort Medienbruch)
  • Die DAOs sind Methoden-lokal UND ich muss keine setter deklarieren (SETTER INJECTION) UND ich brauche keinen speziellen Konstruktor (CONSTRUCTOR INJECTION) UND ich brauche keine speziellen Parameter (PARAMETER INJECTION)
Ich habe etwas weniger Granularität, weil ich DAOs einer Factory gemeinsam betrachte, aber das ist auch in dem Sinne der abstrakten Fabrik.
Naja, man könnte Di auch per Annotation nutzen -> keine Setter, keine Parameter, aber keine einfache Möglichkeit in tests mockobjects zu injecten.
Persönlich finde ich Setter in Implementierungen nicht so schlimm.
Deine tests werdden aber klomplizierter weil du DI und ResourceLookup kombinierst, du musst ejtzt auch deine abstrakte Fabrik konfigurieren, das müsstest du nciht ohne resourceLookup.
Wie gesagt, DI und Resourcelookup leisten in etwa ähnliches, wobei resourcelookup mehr Nachteile hat.

Ich sage, es soll sich wie ein PersistentObject verhalten, mehr nicht, Stichwort Polymorphie. Die Abbildung auf eine DB-Tabelle ist transparent für jeden Aufrufer.
Denke du würdest eigentlich einen sog. DomainService brauchen, mehr dazu später.

Die Aufrufe sind so lokal wie möglich (Stichwort Sichtbarkeit) , denn nur die Methode kennt die DAOs, die sie selber benötigt. Des Weiteren ist die Kopplung lose, weil PMSystemDAO und ProjectDAO Interfaces sind. Ach ja: DAOFabrik ist auch ein Interface. SingletonDAOFabrik ist eine Hilfsklasse, die selber keine DAOFabrik ist, sondern sich eine Singleton-DAOFabrik holt. Die Inderektion ist der losen Kopplung geschuldet.

Kohäsion vs. Lose Kopplung.
Die zus. indirektion kannst du dir imho sparen, hast dann sogar eine noch losere Kopplung.

Ich bin mir im Moment nicht sicher, ob wir dasselbe unter Objekt, Entität, Value Object oder DTO verstehen.
Tatsache ist: Fachlich soll der Name eindeutig sein. Technisch kriegt das Objekt natürlich irgendeine DB-Id verpasst.

Ein DTO ist es definitiv nicht. Ein ValueObject ist aus meiner Sicht nur ein DatenContainer, der wenig bis keine interne Logik hat und das Prinzip der Kapselung minimiert. Eine Entität ist irgendetwas, was eine Identität und einen Typ hat. und ein Objekt ist für mich eine Entität mit Logik, welche das Prinzip der Kapselung maximiert. Ich spreche wahrscheinlich von Objekten.
Respository, DTO, ValueObject, Entity, DomainService etc. pp. sind allesamt Begriffe, die von Domain Driven Design definiert (bzw. umdefiniert wie bei Entity) wurden.
Das Problem: Die Sun Jungs haben "ValueObject" nochmal redefiniert, bis heute immer noch eine Quelle für Missverständnisse.
Das wurde schon vor Jahren korrigiert, in Sun Dokumenten heisst das jetzt auch DTO, ein ValueObjects ist etwas anderes (am besten Mutable, wird durch den Werrt, nicht durch eine ID identifiziert).
In dem Link oben zur J2EE Architektur wird ValueObject noch falsch verwendet...

Persönlich denke ich dass du auch dem richigen Weg bist OO Systeme zu entwicklen, denn die von Sun vorgeschlagene Architektur ist eher prozedural:
- dumme Datenstrukturen ohne Logik wie JavaBeans anstatt echter Objekte,also eine Trennung von Daten und Logik
- die Logik findet sich in Sessionbeans, Martin Fowler nennt das Transaction Script
-die Businesstier stellt per SessionBeans Operationen zur Verfügung welche von der GUI in der richtigen Reihenfolge aufgerufen werden müssen, Evans nennt das GUI driven).

Ich kann dir nur sehr empfehlen, Eric Evans buch über Domain Driven Design zu lesen, geht in genau die Richtung in die du möchtest, ist das Standardwerk zu DDD und definiert Dinge wie DTO, ValueObject eindeutig.

zB. hättest du dann kein DAO, sondern ein Repository, Repos werden von Domain Objekten (Entities, DomainServices, etc. pp.) verwendet, wie DAOs, aber letztere werden laut Sun nur von SessionBeans genutzt ;)
Zur Erzeugung von komplexen Objekten wie Entites (manchmal auch ValueObjects) werden explizite Factories genutzt, zum finden/ändern/persistieren eben Repositories.
Entites sind nicht immutable (wäre sehr unsprakisch beim ändern & specihern), sind aber aus immutable ValueObjects zusammengesetzt und haben selber nicht so viele mutable "Anteile".
Da gibt es noch viel mehr und ich denke dass du es nicht bereuen wirst es zu lesen.

Stark vereinfacht könnte man sagen, dass die "architektonische Welt" (wenn man das so nenne mag) ist in Java in zwei Kontinente geteilt ist:
Suns Empfehlungen und Domain Driven Design.

Wenn du DDD praktizierst (und IMHO tust du das bzw. gehst sehr in diese Richtung), solltest du am besten auch die DDD Terminlogie verwenden, sonst wird es immer einen Aufschrei derer geben, die Suns Empfehlungen folgen, denn da ist es eine Todsünde einer Entity ein DAO mitzugeben und verwenden zu lassen.
In DDD dagegen ist es die absolute Norm, Entites Repositories und Factories mitzugeben, so dass sie selber andere Entites/ValueObjects suchen, erstelllen, ändern und speichern können.

Ach ja, fast vergessen:
Um die Verwirrung komplett zu machen, hat irgend ein Marketing Hans-Wurst mal den Begriff DDD umdefiniert bzw. das versucht, gemeint sind Entites, die statische Methoden zum suchen, speichern bieten.
Das heisst eigentlich "Active Record" bei Fowler und hat nix mit DDD zu tun.

Zu guter letzt:
DDD ist nicht immer angebracht bzw. das beste Mittel, genausowenig wie Suns Empfehlungen/Prozedural immer die beste Lösung darstellt.
Man sollte beides kennen, unterscheiden und anwenden können :)

Der Lock reichert die Methode um Multi-User-Behandlung an. Die Asymmetrie ist zweifelsfrei gegeben. Aktuell sehe ich aber keine andere Möglichkeit, weil ich nicht weiss, woher Fachmethoden später überall her aufgerufen werden und der Aufrufer die Methode in seinem ganz eigenen größeren transaktionalen Kontext aufrufen möchte. Deshalb erfolgt der De-Lock immer am Ende der Transaktion implizit und nicht explizit.
Dasselbe problem hat man auch mit Transaktionen, wenn du das auch deklerativ zB. per Annotation lösen könntest waäre das Ideal, ist aber nicht wirklich so schlimm.
 
Zuletzt bearbeitet von einem Moderator:

Ähnliche Java Themen

Neue Themen


Oben