Generics Tradeoff

F

Firephoenix

Gast
Folgendes Problem (eher Designdiskussion als Problem):

Ich habe eine Persistenzschicht die aktuell für 3 Typen X Y und Z verwendet wird.
Alle Typen in einer Klasse zu verwalten bietet sich an, da meine basisschicht nicht typsicher ist - ich kann also intern alle Klassen über einen Kamm scheren und spare gleichzeitig doppelten code.

Das Designproblem entsteht bei dem Interface der Persistenzeinheit, aktuell sieht es von den Schnittstellen etwa so aus:
-save(X);
-save(Y);
-save(Z);
-update(X);
-...
-remove(X);

weiterhin können observer hinzugefügt werden die über die Operationen der Typen informiert werden (z.B. kann ObserverX informiert werden wenn ein Objekt X gespeichert oder entfernt wird, gleiches für ObserverY und Observer Z).

In meiner Clientschicht macht sich das ganze sehr schön, eine View für X Y und Z implementiert einfach ObserverX, ObserverY und ObserverZ (die objekte gehören semantisch zusammen und müssen gemeinsam dargestellt werden) und registriert sich selbst für alle 3 Observertypen bei der Persistenzeinheit.

Dafür bekomme ich in der Persistenzeinheit logischerweise pro Entity redundanten Code.

Für den Architekturentwurf würde ich selber auf Generics zurückgreifen, mit einer generische Persistenzschicht die die Methoden
-save(T)
-update(T)
-remove(T)
-registerObserver(Observer<T>)

anbietet.
Dann muss allerdings meine tatsächliche Persistenzeinheit 3 fast identische adapterklassen beeinhalten, da ich nicht Persistence<X> Persistence<Y> und Persistence<Z> gleichzeitig implementieren kann.
Weiterhin kann mein view auch nicht Observer<X>, Observer<Y> und Observer<Z> implementieren.
Ich würde also auch hier jeweils einen Adapter benötigen.

Alternativ könnte ich meine typsicherheit über Bord werfen und einfach bei allen Client-Anwendungen casten was definitiv kein schöner Stil ist.

Was wäre eure bevorzugte Lösung?
1. Ein Observer und 3 fast identische Methoden pro Entity-Typ
2. Eine Adapterklasse und ein Adapterobserver pro Entity
3. Verzicht auf Typsicherheit
4. ?

Gruß
 
B

Beni

Gast
Ich finde Typsicherheit ziemlich wichtig (Compilerfehler sind besser als Laufzeitfehler), und würde wohl ohne gross nachzudenken Generics nutzen und mehrere Adapter implementieren.

Aber ich würde auch versuchen, alles was identisch ist in eine abstrakte Super-Klasse auszulagern. Und in diesen Adapter-Klassen dann nur noch den Teil schreiben, der halt nicht identisch ist.
 
B

bygones

Gast
falls die klassen nichts miteinander zu tun haben kannst du auch ein marker interface nutzen und dass in der Persistenzschicht angeben.
 

Ullenboom

Bekanntes Mitglied
Typsicherheit finde ich absolut wichtig, die Frage ist nur, ob Generics nach außen gezeigt werden oder nicht. In der Regel hat man ja spezielle Services, im DDD genannt Repositories, die so was anbieten wie

CustomerService:
saveCustomer( Customer c )

InvoiceService:
saveInvoice( Customer c )

oder so ..

Intern kann doch ruhig etwas gecastet werden, sieht ja keiner. Oder wenn z.b. eine gemeinsame Oberklasse (mit Generics und save(T)) arbeitet.
 
F

Firephoenix

Gast
Umgesetzt wurde jetzt folgende Lösung:
-Interne Persistenzschicht die von 3 Adaptern verwendet wird. Die Adapter sind jeweils Verantwortlich für ein Entity und akzeptieren einen Observer für genau ihren Datentyp. Damit habe ich zumindest mal Single-Responsibility was in dieser Architektur vorrang hat vor OCP gegenüber neuen Entities.
Weiterhin wurde eine Klasse eingerichtet die den Adapter für jedes Entity zurückgeben kann (bei 3 Methoden noch übersichtlich, möglich wäre auch sowas wie
PersistenceAdapter<T> adapterFor(Class<T>) mit Exception falls die Klasse nicht vorhanden ist - aber das ist aktuell eher ein Overkill und wird erst dann interessant wenn ich mal 10+ Entities verwalten muss.
Das ist allerdings eine unwahrscheinliche Erweiterung. Zumindest gibt die Klasse dem Benutzer die Möglichkeit leicht an den passenden Adapter zu kommen, sie hat auch die bisherige Schicht ersetzt.

In der GUI hat die Änderung den Code deutlich verbessert, zwar benötige ich jetzt die angesprochenen Adapter, aber die Verantwortlichkeiten der Klassen sind wesentlich feingranularer abgebildet und besser zu warten. Als einziger Nachteil wäre zu erwähnen, dass die Methodenaufrufe aktuell eine Ebene Tiefer gerückt sind da aus (persistenzschicht.update(T) jetzt sowas wie neueKlasse.adapterFürT().update(T)) geworden ist, aber das ist eher ein Refactoring-Fall, da die meisten Klassen nur einen oder 2 Adapter benötigen und man hier einiges spart wenn man statt der Sammelklasse direkt den Adapter durchgibt.

Danke für die guten Bewertungen der Möglichkeiten.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
mimo Generics Softwareentwicklung 2

Ähnliche Java Themen

Neue Themen


Oben