4 Stimmen

Behebung eines Anti-Musters in der Aufrufkette

Ich habe begonnen, etwas von einem Anti-Muster in meiner ASP.NET-Entwicklung zu bemerken. Es stört mich, weil es sich wie das Richtige anfühlt, um gutes Design zu erhalten, aber gleichzeitig riecht es falsch.

Das Problem ist folgendes: Wir haben eine mehrschichtige Anwendung, die unterste Schicht ist eine Klasse, die Anrufe an einen Dienst verarbeitet, der uns mit Daten versorgt. Darüber befindet sich eine Schicht von Klassen, die die Daten transformieren, manipulieren und überprüfen können. Darüber liegen die ASP.NET-Seiten.

In vielen Fällen müssen die Methoden der Dienstschicht nicht geändert werden, bevor sie in der Ansicht angezeigt werden, so dass das Modell einfach durchlaufen werden kann, z. B:

public List<IData> GetData(int id, string filter, bool check)
{
    return DataService.GetData(id, filter, check);
}

Das ist nicht falsch und auch nicht unbedingt schlimm, aber es schafft eine seltsame Art von Copy/Paste-Abhängigkeit. Ich arbeite auch an dem zugrundeliegenden Dienst, der dieses Muster ebenfalls häufig wiederholt, und es gibt überall Schnittstellen. Was also passiert, ist: "Ich muss Folgendes hinzufügen int someotherID a GetData " Ich füge es also dem Modell, dem Dienstaufrufer, dem Dienst selbst und den Schnittstellen hinzu. Es hilft nicht, dass GetData steht eigentlich stellvertretend für mehrere Methoden, die alle dieselbe Signatur verwenden, aber unterschiedliche Informationen zurückgeben. Die Schnittstellen helfen ein wenig bei dieser Wiederholung, aber sie taucht immer noch hier und da auf.

Gibt es einen Namen für dieses Anti-Muster? Gibt es eine Lösung, oder ist eine größere Änderung der Architektur die einzige wirkliche Möglichkeit? Es klingt, als müsste ich mein Objektmodell abflachen, aber manchmal führt die Datenschicht Transformationen durch, so dass sie einen Wert hat. Ich mag es auch, meinen Code zwischen "ruft einen externen Dienst" und "liefert Seitendaten" zu trennen.

0voto

Tengiz Punkte 7291

Eigentlich ist der Weg, den Sie gewählt haben, der Grund dafür, dass Sie haben, was Sie haben (ich sage nicht, dass es schlecht ist).

Zunächst möchte ich sagen, dass Ihr Ansatz ganz normal ist.

Lassen Sie mich nun Ihre Schichten durchdenken:

  • Ihr Dienst - bietet eine Art von stark typisiertem Zugriffsmodell. Was das bedeutet, ist es hat einige Arten von Argumenten, verwendet sie in einige spezielle Arten von Methoden, die wieder einige spezielle Art von Ergebnissen zurück.
  • Ihre Service-Zugangsschicht - bietet ebenfalls die gleiche Art von Modell. Sie nimmt also besondere Arten von Argumenten für besondere Arten von Methoden entgegen und gibt besondere Arten von Ergebnissen zurück.
  • usw...

Um keine Verwirrung zu stiften, nenne ich die folgenden Begriffe besondere Art :

public UserEntity GetUserByID(int userEntityID);

In diesem Beispiel müssen Sie genau Int übergeben, während Sie genau GetUserByID aufrufen, und es wird genau die UserEntity Objekt.

Nun eine andere Art von Ansatz :

Erinnern Sie sich, wie SqlDataReader funktioniert? nicht sehr stark getippt, richtig? Was Sie hier fordern, ist meiner Meinung nach, dass Sie eine nicht stark typisierte Schicht vermissen.

Um das zu erreichen, müssen Sie irgendwo in Ihren Schichten von der starken Typisierung zur nicht starken Typisierung wechseln.

Example :

public Entity SelectByID(IEntityID id);
public Entity SelectAll();

Wenn Sie also so etwas anstelle der Dienstzugriffsschicht hätten, könnten Sie es für jedes beliebige Argument aufrufen.

Aber, das ist fast die Erstellung eines ORM von Ihrem eigenen, so würde ich nicht denken, dies ist der beste Weg zu gehen.

0voto

George Polevoy Punkte 7073

Es ist wichtig zu definieren, welche Art von Verantwortung zu welcher Schicht gehört, und diese Logik nur in der Schicht zu platzieren, zu der sie gehört.

Es ist völlig normal, einfach durchzugehen, wenn Sie in einer bestimmten Methode keine Logik hinzufügen müssen. Irgendwann müssen Sie dies vielleicht tun, und die Abstraktionsschicht wird sich dann auszahlen.

Noch besser ist es, parallele Hierarchien zu haben und nicht nur die Objekte der darunter liegenden Ebene nach oben weiterzugeben, so dass jede Ebene ihre eigene Klassenhierarchie verwendet, und Sie können etwas wie AutoMapper verwenden, wenn Sie das Gefühl haben, dass es keinen großen Unterschied in den Hierarchien gibt. Dies gibt Ihnen Flexibilität, und Sie können die automatische Zuordnung jederzeit durch benutzerdefinierten Zuordnungscode in bestimmten Methoden/Klassen ersetzen, falls die Hierarchien nicht mehr übereinstimmen.

Wenn Sie viele Methoden mit fast der gleichen Signatur haben, dann sollten Sie an das Abfrage-Spezifikationsmuster denken.

IData GetData(IQuery<IData> query)

In der Präsentationsschicht können Sie dann einen Databinder für Ihre benutzerdefinierten Abfragespezifikationsobjekte implementieren, wobei ein einzelner Aspnet-Handler die Erstellung spezifischer Abfrageobjekte implementieren und diese an eine einzelne Servicemethode weiterleiten könnte, die sie an eine einzelne Repository-Methode weiterleitet, wo sie entsprechend einer spezifischen Abfrageklasse, möglicherweise mit einem Visitor-Muster, abgefertigt werden kann.

IQuery<IData> BindRequest(IHttpRequest request)

Mit diesem Automapping- und Abfragespezifikationsmuster können Sie Duplikate auf ein Minimum reduzieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X