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.

3voto

Can Gencer Punkte 8562

Ich würde vorschlagen, dass Sie die Abfrageobjektmuster um dieses Problem zu lösen. Im Grunde genommen könnte Ihr Dienst eine Signatur haben wie:

IEnumerable<IData> GetData(IQuery<IData> query);

Innerhalb der IQuery-Schnittstelle könnten Sie eine Methode haben, die eine Arbeitseinheit als Input nimmt, zum Beispiel einen Transaktionskontext oder etwas wie ISession, wenn Sie einen ORM wie NHibernate verwenden, und eine Liste von IData-Objekten zurückgibt.

public interface IQuery<T> 
{
 IEnumerable<T> DoQuery(IUnitOfWork unitOfWork);
}

Auf diese Weise können Sie stark typisierte Abfrageobjekte erstellen, die Ihren Anforderungen entsprechen, und haben eine saubere Schnittstelle für Ihre Dienste. Dieser Artikel von Ayende ist eine gute Lektüre zu diesem Thema.

1voto

Robert Rossney Punkte 91100

Für mich klingt das so, als bräuchten Sie eine weitere Schnittstelle, so dass die Methode in etwa so aussieht:

public List<IData> GetData(IDataRequest request)

0voto

Jon Hanna Punkte 106367

Sie delegieren an eine andere Ebene, und das ist nicht unbedingt etwas Schlechtes.

Sie könnten hier oder in einer anderen Methode eine andere Logik hinzufügen, die nur in diese Ebene gehört, oder die Ebene mit einer anderen Implementierung delegieren, so dass die betreffenden Ebenen durchaus sinnvoll genutzt werden könnten.

Vielleicht haben Sie zu viele Schichten, aber ich würde das nicht nur aus diesem Blickwinkel heraus sagen, sondern weil ich sonst nichts sehe.

0voto

MattC Punkte 3914

Nach dem, was Sie beschrieben haben, klingt es einfach so, als ob Sie in Ihrer Anwendung auf einen der "Kompromisse" der Abstraktion gestoßen sind.

Nehmen wir den Fall an, dass diese "Aufrufketten" die Daten nicht mehr "durchlaufen", sondern umgewandelt werden müssen. Dies mag jetzt nicht erforderlich sein, und man kann sich sicherlich dafür aussprechen YAGNI .

In diesem Fall scheint der technische Aufwand jedoch nicht allzu groß zu sein, mit dem positiven Nebeneffekt, dass Änderungen an den Daten zwischen den Ebenen leicht vorgenommen werden können.

0voto

7wp Punkte 12253

Ich verwende dieses Muster auch. Allerdings habe ich es für den Zweck der Entkopplung meiner Domänenmodellobjekte von meinen Datenobjekten verwendet.

In meinem Fall "durchlaufe" ich das Objekt aus der Datenebene nicht, wie Sie es in Ihrem Beispiel tun, sondern "ordne" es einem anderen Objekt zu, das sich in meiner Domänenebene befindet. Ich verwende AutoMapper um sich die Mühe zu ersparen, dies manuell zu tun.

In den meisten Fällen sieht mein Domänenobjekt genau so aus wie das Datenobjekt, aus dem es entstanden ist. Es kommt jedoch vor, dass ich Informationen aus meinem Datenobjekt reduzieren muss... oder dass ich nicht an allem interessiert bin, was in meinem Datenobjekt enthalten ist usw. Ich ordne das Datenobjekt einem benutzerdefinierten Domänenobjekt zu, das nur die Felder enthält, an denen meine Domänenebene interessiert ist.

Dies hat auch den Nebeneffekt, dass, wenn ich mich entschließe, meine Datenschicht für etwas anderes umzuformulieren oder zu ändern, meine Domänenobjekte davon nicht betroffen sind, da sie durch die Mapping-Technik entkoppelt sind.

Hier ist eine Beschreibung des Auto-Mappers, der sozusagen das ist, was dieses Entwurfsmuster zu erreichen versucht, denke ich:

AutoMapper ist auf Modellprojektionsszenarien ausgerichtet, um komplexe Objektmodelle auf DTOs und andere einfache Objekte zu reduzieren, deren Design sich besser für Serialisierung, Kommunikation, Messaging oder einfach als Anti-Korruptionsschicht zwischen der Domänen- und Anwendungsschicht eignet.

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