Bezeichner gegen Domänenobjekt als Methodenparameter

10

Gibt es objektive Argumente für oder gegen die Verwendung von Objekten gegen eindeutige ID als Methoden- / Funktionsparameter? (und Mitglieder anderer Objekte?). Speziell im Kontext statisch typisierter Sprachen (C # / Java / Scala)

Vorteile des Objekts selbst:

  • Weitere typsichere Anrufe. Bei IDs besteht die Gefahr einer falschen Reihenfolge der Argumente. Dies kann jedoch gemindert werden, indem für jede Klasse eine 'Mini'-Klasse beibehalten wird, in der nur die ID dieser Klasse gespeichert ist.
  • Holen Sie sich einmal aus Ausdauer, keine Notwendigkeit, wieder zu bekommen
  • Wenn sich bei ID der ID-Typ ändert, z. B. int -> long, ist eine allgemeine Änderung mit der Möglichkeit von Fehlern erforderlich. (Courtsey: https://softwareengineering.stackexchange.com/a/284734/145808 )

Vorteile der Verwendung von ID:

  • Meistens wird das eigentliche Objekt nicht benötigt, sondern nur eine eindeutige ID. Wenn Sie also eine ID haben, sparen Sie Zeit, wenn Sie es aus der Persistenz herausholen.

Eine Mischung dieser Techniken hätte, soweit ich sehen kann, nur Nachteile von beiden und Vorteile von beiden.

Da dies ein konkret definiertes Problem ist, hoffe ich, dass es objektive Antworten gibt und nicht die Typen "Ich denke" oder "Ich mag" ... :-).

BEARBEITEN: Kontext auf Vorschlag eines Kommentators - Die einzige Einschränkung sind statisch typisierte Sprachen. Die Anwendung ist eine Allzweckanwendung, freut sich jedoch über Antworten, die auch auf bestimmten Verwendungsszenarien basieren.

EDIT: Noch etwas Kontext. Angenommen, ich habe ein Buchverwaltungssystem. Mein Modell ist:

Book: { id: Int, isbn: String, donatedBy: Member, borrowedBy: Member }
Member: {id: Int, name: String}

Table- wishlist: { isbn: String, memberId: Int}
Table- borrows:  { id: Int, memberId: Int}  

Method: 
isInWishList( book: Book, member: Member ) vs isInWishList( bookId: Int,  memberId: Int)

handleBorrowRequest( memberId: Int, book: Book ){ 
   //the login system doesn't actually get the entire member structure, only the member id. I don't need the full member yet. can run a query to verify that the memberId has less than max allowed books for borrowing. 
}

Warum sollte ich nur den Ausweis und nicht das Vollmitglied behalten wollen? Wenn ich Informationen über ein Buch erhalte, muss ich selten wissen, wer es gespendet oder ausgeliehen hat. Es ist ein Overkill, die vollständigen Informationen zu erhalten, um die Felder "SpendedBy" und "BorrededBy" zu füllen.

Natürlich sind dies nur meine Punkte. Ich bin mir sicher, dass mir Dinge fehlen, also wollte ich wissen, was die Punkte dafür / dagegen sind.

0fnt
quelle
Könnten Sie Ihre Frage mit etwas mehr Kontext bearbeiten? Es ist nicht klar, was das andere Szenario ist. Ich werde erraten , dass Sie über Objekte reden , die von einem ORM - System verwaltet werden (wie Hibernate) und dem von „Mini-Klasse“ meinen Sie ein Lazy-Laden - Proxy-Objekt.
Darien
2
Möglicherweise verwandt? programmers.stackexchange.com/questions/284634/…
hjk
Hinzufügen als Kommentar hier (bis ich genug Kontext bekommen kann auf eine voll funktionsfähige Antwort zu erweitern): Es sei denn , Sie sind hart codierte ID - Werte in Ihrem Code (ein großes no-no), werden Sie noch Ihre ID - Werte laden müssen von irgendwo richtig? "Meistens" ist auch ziemlich vage IMHO ...
hjk
@hjk Mehr Kontext hinzugefügt, danke. Sie haben einen Punkt - Sie müssen immer noch ID-Werte von irgendwoher laden. Es geht jedoch nicht darum, die Datenbank nicht zu berühren, sondern die Nutzung und Bandbreite der Datenbank zu minimieren. Ich kann Ausweise von allen bekommen, die ein Buch ausleihen möchten, aber tatsächlich wäre es unnötig, ihre vollständigen Details zu erhalten.
0fnt
Es scheint, dass dies von mehreren Dingen abhängen würde: (1) ob die Anwendungsfälle die tatsächlichen Attribute (Spalten) oder nur die ID erfordern, was von diesen Anwendungsfällen selbst abhängt; (2) ob Sie einige Caching- oder leistungssteigernde Techniken verwenden, die die Abfrage von ID zu Attributen mühelos machen würden, und ob Ihre Anwendungsfälle schwer genug wären, um solche Caching-Schemata zu brechen.
Rwong

Antworten:

8

Abhängig von Ihrer Umgebung und Ihrem Problemkontext kann die Notwendigkeit, in die Datenbank zu wechseln, verringert werden. Infolgedessen geht das Argument (IMO) für die Verwendung nur von IDs verloren.

Zum Beispiel hat PHPs Doctrine2 ORM EntityManager::getReference, das einen träge geladenen Proxy für eine Entität unter Verwendung der angegebenen ID erzeugt; Ich weiß nicht, wie viele andere ORMs das tun, aber es entspricht in etwa der von Ihnen erwähnten Vorstellung einer "Miniklasse". In den meisten Fällen handelt es sich um eine vollständig hydratisierte Einheit, sodass Sie sie weitergeben und wie jede andere verwenden können.

Der einzige Fall, in dem dies nicht der Fall ist, ist die Angabe einer ungültigen ID. In diesem Fall möchten Sie zur Validierung trotzdem zur Datenbank gehen. Um die Kosten für das Abrufen von Entitäten zu senken, verfügen die meisten ORMs über Caching-Funktionen (erste und zweite Ebene), die in irgendeiner Form verfügbar sind.

Sobald wir den Bedarf und die Kosten für den Zugriff auf die Datenbank reduziert haben, bleibt uns weitgehend der Vorteil, die Entität als Parameter zu verwenden:

  1. Typensicherheit.
  2. Weniger Kenntnisse für den Anrufer erforderlich. Ein Entwickler, der 6 Monate später arbeitet, kann nicht fälschlicherweise die ID der falschen Entität eingeben.
  3. Reduzierte Refactoring-Kosten. Wenn eine Methode geändert werden sollte, um zwei Informationen von der Entität zu verlangen, entstehen keine Kosten, wenn der Anrufer geändert wird, um sie bereitzustellen, vorausgesetzt, der Anrufer hat zunächst die vollständig hydratisierte Entität.
  4. Pro Methode ist weniger Validierung erforderlich. Viele Methoden können davon ausgehen, dass die ihnen angegebene Entität in der Datenbank vorhanden ist (da sie aus dem ORM stammt) und daher die ID nicht mehr validieren müssen.
  5. (Potenziell) weniger Datenbankaufrufe. Wenn Sie IDs an drei Methoden übergeben und jede diese validieren oder mehr Daten für die Entität abrufen muss, sind dies dreimal so viele Datenbankaufrufe wie eine Methode, die die Entität abruft, und zwei, die sie bearbeiten.

Natürlich gibt es Fälle, in denen man möglicherweise nur eine ID übergeben möchte: zum Beispiel beim Überschreiten einer begrenzten Kontextgrenze (im domänengesteuerten Design sprechen). Wenn wir zwei begrenzte Kontexte mit unterschiedlichen Vorstellungen davon betrachten, was ein Konto ausmacht (z. B. Finanzen oder Kundenbeziehungen), können wir das Konto von Kontext A nicht an Kontext B übergeben. Stattdessen kann eine Konto-ID (in der Sprache der Domäne) verwendet werden übergangen werden.

Andy Hunt
quelle
Das Caching lässt sich nicht gut skalieren, wenn mehrere Instanzen von Anwendungen ausgeführt werden. Ich habe mein eigenes Caching eingerichtet. Die Punkte sind jedoch ausgezeichnet.
0fnt
Ich denke, ein weiterer Punkt gegen IDs ist, dass die Verwendung von ID neben der Validierung auch das Aufrufen der Persistenz impliziert, um das Objekt tatsächlich zu erhalten.
0fnt
3

Für diese Art von Frage delegiere ich an die Lösung, die meine Absicht als Programmierer am besten kommuniziert, und mache die Arbeit von "Future Greg" am einfachsten.

Die Verwendung des Objekts als Argument erleichtert es, den Methodenaufruf in 6 Monaten richtig zu machen, und ich habe die Details dieser Anwendung vergessen. Um auf Ihrem "Ist dieses Buch im Wunschliste-Beispiel dieser Person" aufzubauen, nehmen wir an, dass die isInWishListMethode wirklich nur die ganzzahligen IDs von Büchern und Mitgliedern vergleichen muss. In der Realität benötigt diese eine Methode nur zwei Daten, um ihre Aufgabe zu erfüllen. Lassen Sie uns nun einen Schritt von dieser einen Methode zurücktreten und das gesamte Softwaresystem betrachten. Ich bezweifle, dass für den Betrieb nur eine Buch-ID und eine Mitglieds-ID erforderlich sind. Sie werden ohnehin das vollständige Buch und Mitglied aus der Datenbank ziehen, bevor Sie das aufrufen, isInWishListda Sie wahrscheinlich mehr tun müssen, als nur diese eine Methode aufzurufen. Vielleicht tust du das, aber meistens musst du mehr tun.

Angesichts dessen werden Sie nicht realistisch einen Leistungsverlust erleiden, wenn Sie beide Objekte vollständig aus der Datenbank abrufen und zuordnen. Theoretisch benötigen Sie weniger Datenbankaufrufe, aber in der Praxis werden Sie feststellen, dass dies einfach nicht stimmt. Wenn Sie diese IDs als Parameter in der Abfragezeichenfolge an den Server zurückgeben, müssen Sie nur die Wunschliste aus der Datenbank abrufen. Dies ist jedoch eine sehr enge Ansicht Ihrer gesamten Anwendung. Es gibt Szenarien, in denen Sie neben der Überprüfung einer Wunschliste auch andere Vorgänge ausführen, z. B. ein Buch zu einer Wunschliste hinzufügen. Sie möchten keine doppelten Elemente hinzufügen.

Entscheiden Sie sich für die Lösung, die für einen neuen Programmierer oder Ihr zukünftiges Selbst am einfachsten zu verstehen ist, nämlich das Übergeben von Bookund MemberObjekten. Erst nachdem Sie ein tatsächliches Leistungsproblem gefunden haben, sollten Sie sich wirklich darum kümmern, nur die IDs zu übergeben.

Oder wir können uns daran erinnern, dass statisch typisierte Sprachen wie Java eine Methodenüberladung bieten, sodass Sie Ihren Kuchen haben und ihn auch essen können:

public boolean isInWishList(int bookId, int memberId) {
    // ...
}

public boolean isInWishList(Book book, Member member) {
    return isInWishList(book.getId(), member.getId());
}

Die eigentliche Antwort auf diese Frage besteht darin, beide Methoden zu schreiben, die Nur-Ganzzahl-Version aus der stark typisierten Version aufzurufen und Bob ist Ihr Onkel.

Greg Burghardt
quelle