Wie kann verhindert werden, dass doppelte Datenzugriffsmethoden zum Abrufen ähnlicher Daten verwendet werden?

8

In fast jedem Projekt, an dem ich mit einem Team arbeite, scheint sich das gleiche Problem einzuschleichen. Jemand schreibt UI-Code, der Daten benötigt, und schreibt eine Datenzugriffsmethode:

AssetDto GetAssetById(int assetId)

Eine Woche später arbeitet jemand anderes an einem anderen Teil des Antrags und benötigt ebenfalls einen, AssetDtoaber jetzt auch "Genehmiger" und schreibt Folgendes:

AssetDto GetAssetWithApproversById(int assetId)

Einen Monat später benötigt jemand einen Vermögenswert, der jedoch jetzt die "Fragen" (oder die "Eigentümer" oder die "laufenden Anfragen" usw.) enthält:

AssetDto GetAssetWithQuestionsById(int assetId)
AssetDto GetAssetWithOwnersById(int assetId)
AssetDto GetAssetWithRunningRequestsById(int assetId)

Und es wird noch schlimmer, wenn Methoden wie GetAssetWithOwnerAndQuestionsByIdbeginnen zu erscheinen.

Sie sehen das Muster, das entsteht: Ein Objekt wird an ein großes Objektdiagramm angehängt, und Sie benötigen verschiedene Teile dieses Diagramms an verschiedenen Stellen.

Natürlich möchte ich verhindern, dass es eine große Anzahl von Methoden gibt, die fast dasselbe tun. Ist es einfach eine Frage der Teamdisziplin oder gibt es ein Muster, mit dem ich dies verhindern kann? In einigen Fällen kann es sinnvoll sein, separate Methoden zu verwenden, dh es kann teuer sein, ein Asset mit laufenden Anforderungen zu erhalten, sodass ich diese nicht immer einbeziehen möchte. Wie gehe ich mit solchen Fällen um?

Ronald Wildenberg
quelle
1
Sie können so etwas wie Grails verwenden, das das verzögerte Laden (Gorm über den Ruhezustand) von Eigenschaften handhabt - wenn versucht wird, darauf zuzugreifen. Auf diese Weise müssen Sie nur a = getAssetById(x)a.questions usw. aufrufen und können sie dann aufrufen, ohne sie speziell zu laden, da das zugrunde liegende ORM-System sie für Sie lädt, wenn der Zugriff versucht wird.
Techfoobar
1
Dies wäre möglich, erfordert jedoch, dass Sie während der Abfrage einen Datenbankkontext offen halten. Ich möchte lieber nicht, dass diese Art von Wissen aus der Datenzugriffsschicht austritt. Und Sie haben weniger Kontrolle über ausgeführte Abfragen. Aber eine sehr interessante Option ...
Ronald Wildenberg
Ja, und das ist wohl der springende Punkt bei der Trennung in DTOs. Grails geht für die Nicht-DTO-Methode.
Techfoobar
Das Ausführen offener Abfragen von einer einzigen Schnittstelle aus würde erfordern, dass diese Schnittstelle eine domänenspezifische Abfragesprache ist. Die akzeptierte Antwort ist ähnlich.
Mike30

Antworten:

4

In Bezug auf die Syntax würde ich ein Zwischenobjekt zum Erstellen von Abfragen mit einer fließenden Schnittstelle erstellen:

// all the basic, cheap to query fields
AssetDto a = AssetRetriever(asset_id).fetch() 

// some common expensive fields
AssetDto a = AssetRetriever(asset_id).withOwner().withQuestion().fetch() 

// numerous less common fields may not command dedicated methods
AssetDto a = AssetRetriever(asset_id).withFields("foo", "bar").fetch() 

// Better yet, use an enum and enjoy static checking
AssetDto a = AssetRetriever(asset_id).withFields(F_OWNER, F_QUESTION).fetch() 

Ich hoffe, es ist offensichtlich genug, um es umzusetzen. Die einzige Methode, die die Datenbank tatsächlich berührt, ist fetch().

9000
quelle
Diese Lösung gefällt mir sehr gut. Es erzwingt, was ich mit einer verständlichen und sauberen Syntax erreichen möchte, und bietet viele Möglichkeiten zur Erweiterung der Schnittstelle und zur Optimierung von SQL-Abfragen.
Ronald Wildenberg
2

Beim Umgang mit großen Objekten ist dies sehr häufig. Das Hinzufügen neuer Methoden erhöht zwar die Leistung, verringert jedoch die Wartbarkeit erheblich. Und wieder müssen Sie zwischen diesen beiden wählen.

Ich schlage vor, Sie haben eine Methode, die häufig verwendete Daten zurückgibt (nicht unbedingt die kleinsten), eine andere, die das gesamte Objekt zurückgibt, und wahrscheinlich einige mehr für die teuersten Ressourcen.

Ein anderer Ansatz besteht darin, Methoden zu haben, die nur die erforderlichen Felder des Objekts zurückgeben, wie z . B. AssetQuestions GetAssetQuestionsById(int assetId)oder Owners GetAssetOwnersById(int assetId).

Gleichzeitig müssen Sie einige Regeln für das Abrufen von Daten festlegen. Wenn beispielsweise jemand 5 Felder des Objekts benötigt und eine vorhandene Methode 8 zurückgibt, sollte die vorhandene Methode verwendet werden.

superM
quelle
Ich hoffte, dass es ein Muster geben würde, das ich durchsetzen könnte, damit das Problem nicht auftritt, aber ich fürchte, Ihre Lösung ist die beste. Es kommt auf mehr Disziplin und Forschung an, wenn Sie neuen Datenzugriffscode schreiben.
Ronald Wildenberg
Ich mag Ihre Lösung, die zusätzliche Methoden zum Abrufen verwandter Objekte einführt. Es kann ein wenig Leistung kosten, da Sie keine Daten mehr in der Datenbank verbinden, aber aus Gründen der Wartbarkeit ist dies wahrscheinlich eine große Verbesserung.
Ronald Wildenberg
3
Es gibt eine "verrückte" Lösung, die mir in den Sinn kommt: Erstellen Sie einen Typ mit einem booleschen Feld für jeden Parameter, setzen Sie true für die Felder, die Sie abrufen möchten, und übergeben Sie dieses Objekt als Parameter)))
superM
Das ist mir auch in den Sinn gekommen und der einzige Nachteil ist, dass Sie darauf vorbereitet sein müssen, alle möglichen Kombinationen in Ihrem Datenzugriffscode zu berücksichtigen. Obwohl Sie natürlich sehr effiziente Abfragen für die häufigsten Fälle schreiben könnten ...
Ronald Wildenberg
Ich denke, das wäre auch schwierig zu warten, könnte aber in einigen Fällen
funktionieren
1

Ich habe in letzter Zeit dasselbe Problem durchgemacht und die folgende Lösung gewählt:

Datenzugriffsmethoden sollten nur Daten von einer einzelnen Ressource (z. B. Datenbanktabelle) abrufen. Wenn der Prozess verwandte Objekte benötigt, die an das Hauptobjekt angehängt sind, sollte die für diese jeweiligen Objekte verantwortliche Methode aufgerufen werden.

Auf diese Weise sollten Sie eine Fassadenmethode erstellen, die die Objekte verbindet, wenn Sie ein Asset mit seinen Genehmigenden benötigen.

Beispiel:

public Class AssetFacade {

   public AssetDto getAssetWithQuestionsByAssetId(int assetId) { 

      AssetDto asset = AssetDao.getAssetById(assetId);
      List<QuestionDto> questions = AssetDao.getQuestionsByAssetId(assetId);
      asset.setQuestions(questions);

      return asset;
   };
 }
marcioggs
quelle
1
Dies ist eine mögliche Lösung, aber ich mag es nicht, dass Sie alle Möglichkeiten zur Optimierung des Datenzugriffs über die Datenbank verlieren. Das heißt, eine Abfrage mit einem Join für drei Tabellen kann schneller sein als drei separate Abfragen.
Ronald Wildenberg
Stimmen Sie zu, konnten aber zu diesem Zeitpunkt keine bessere Lösung finden. Ich bin froh, dass du diese Frage für mich erstellt hast, um auch einen besseren Weg zu lernen!
Marcioggs
0

Ist es einfach eine Frage der Teamdisziplin oder gibt es ein Muster, mit dem ich dies verhindern kann?

Ja, es handelt sich um einige Richtlinien für das Benennungsmuster des Teams. Sie können einfache 4 Methoden wie GetEntityById (), GetAllEntities (), SetEntity (), DeleteEntityById () festlegen.

Darüber hinaus können Sie zwei dto's mit der Benennung AssetSimpleDto GetAssetById(assetId)und ein weiteres detailliertes dto mit dem Namen as haben AssetDto GetAssetDetailById(assetId). Die erste Methode und dto sind so angepasst, dass sie nur ein Minimum bieten, während die zweite alle zugehörigen Informationen enthält, die Ihre Funktionalität möglicherweise benötigt.

Yusubov
quelle
Leider ist der Fall nicht so einfach wie "einfach werden" oder "alles bekommen", so dass das zugrunde liegende Problem nicht wirklich gelöst wird. Dies würde wahrscheinlich dazu führen, dass jeder die Methode 'get all' verwendet, da er einen Teil des Objektdiagramms benötigt, der nicht von 'get simple' zurückgegeben wird.
Ronald Wildenberg