Ich möchte einige Fragen zu bewährten Methoden in Bezug auf Zuordnungstypen und die Verwendung von Erweiterungsmethoden in C # stellen. Ich weiß, dass dieses Thema in den letzten Jahren mehrmals diskutiert wurde, aber ich habe viele Beiträge gelesen und habe immer noch Zweifel.
Das Problem, auf das ich gestoßen bin, war die Erweiterung der Klasse, die ich besitze, um die Konvertierungsfunktionalität. Angenommen, ich habe die Klasse "Person", die ein Objekt darstellt, das von einer Logik verwendet wird. Ich habe auch eine Klasse "Kunde", die eine Antwort von einer externen API darstellt (tatsächlich wird es mehr als eine API geben, daher muss ich die Antwort jeder API einem allgemeinen Typ zuordnen: Person). Ich habe Zugriff auf den Quellcode beider Klassen und kann dort theoretisch meine eigenen Methoden implementieren. Ich muss Customer in Person konvertieren, damit ich es in der Datenbank speichern kann. Das Projekt verwendet keine automatischen Mapper.
Ich habe 4 mögliche Lösungen im Auge:
.ToPerson () -Methode in der Consumer-Klasse. Es ist einfach, aber es scheint mir, als würde das Muster der Einzelverantwortung durchbrochen, insbesondere, dass die Consumer-Klasse auch anderen Klassen zugeordnet ist (einige werden von einer anderen externen API benötigt), sodass sie mehrere Zuordnungsmethoden enthalten müsste.
Zuordnungskonstruktor in der Personenklasse unter Verwendung von Consumer als Argument. Auch einfach und scheint auch Single-Responsibility-Muster zu brechen. Ich brauche mehrere Mapping-Konstruktoren (da es Klassen von einer anderen API gibt, die die gleichen Daten wie Consumer liefern, aber in einem etwas anderen Format)
Converters-Klasse mit Erweiterungsmethoden. Auf diese Weise kann ich die .ToPerson () -Methode für die Consumer-Klasse schreiben. Wenn eine andere API mit einer eigenen NewConsumer-Klasse eingeführt wird, kann ich einfach eine andere Erweiterungsmethode schreiben und alles in derselben Datei belassen. Ich habe eine Meinung gehört, dass Erweiterungsmethoden im Allgemeinen böse sind und nur verwendet werden sollten, wenn dies unbedingt erforderlich ist, und das ist es, was mich zurückhält. Ansonsten mag ich diese Lösung
Konverter / Mapper-Klasse. Ich erstelle eine separate Klasse, die Konvertierungen verarbeitet und Methoden implementiert, die die Quellklasseninstanz als Argument verwenden und die Zielklasseninstanz zurückgeben.
Zusammenfassend kann mein Problem auf eine Reihe von Fragen reduziert werden (alles im Zusammenhang mit dem, was ich oben beschrieben habe):
Wird das Platzieren der Konvertierungsmethode in einem (POCO?) - Objekt (wie die .ToPerson () - Methode in der Consumer-Klasse) als Verstoß gegen das Muster einer einzelnen Verantwortung betrachtet?
Wird die Verwendung von Konvertierungskonstruktoren in einer (DTO-ähnlichen) Klasse als Verstoß gegen das Muster einer einzelnen Verantwortung angesehen? Insbesondere wenn eine solche Klasse aus mehreren Quelltypen konvertiert werden kann, wären dann mehrere Konvertierungskonstruktoren erforderlich?
Wird die Verwendung von Erweiterungsmethoden beim Zugriff auf den ursprünglichen Klassenquellcode als schlechte Praxis angesehen? Kann ein solches Verhalten als tragfähiges Muster für die Trennung von Logik verwendet werden oder ist es ein Anti-Muster?
quelle
Person
Klasse ein DTO? Enthält es irgendein Verhalten?Antworten:
Ja, weil die Bekehrung eine andere Verantwortung ist.
Ja, die Konvertierung ist eine andere Verantwortung. Es macht keinen Unterschied, ob Sie dies über Konstruktoren oder über Konvertierungsmethoden (zB
ToPerson
) tun .Nicht unbedingt. Sie können Erweiterungsmethoden auch dann erstellen, wenn Sie den Quellcode der Klasse haben, die Sie erweitern möchten. Ich denke, ob Sie eine Erweiterungsmethode erstellen oder nicht, sollte durch die Art dieser Methode bestimmt werden. Enthält es zum Beispiel viel Logik? Kommt es auf etwas anderes an, als die Mitglieder des Objekts selbst? Ich würde sagen, dass Sie keine Erweiterungsmethode haben sollten, die Abhängigkeiten erfordert oder komplexe Logik enthält. In einer Erweiterungsmethode sollte nur die einfachste Logik enthalten sein.
Wenn die Logik komplex ist, sollten Sie meiner Meinung nach keine Erweiterungsmethode verwenden. Wie ich bereits erwähnt habe, sollten Sie Erweiterungsmethoden nur für die einfachsten Dinge verwenden. Ich würde die Konvertierung nicht einfach finden.
Ich schlage vor, dass Sie Konvertierungsdienste erstellen. Sie können eine einzige generische Schnittstelle dafür haben:
Und Sie können Konverter wie folgt haben:
Und Sie können Dependency Injection verwenden , um einen Konverter (z. B.
IConverter<Person,Customer>
) in jede Klasse zu injizieren , für die die Konvertierung zwischenPerson
und erforderlich istCustomer
.quelle
IConverter
Framework, das nur darauf wartet, implementiert zu werden.IConvertable
, wonach wir hier nicht suchen. Mein Fehler.Converter
wirdList
beim Aufruf von verwendetConvertAll
. msdn.microsoft.com/en-us/library/kt456a2y(v=vs.110).aspx Ich weiß jedoch nicht, wie nützlich dies für OP ist.Ja. Eine
Consumer
Klasse ist dafür verantwortlich, die Daten zu einem Verbraucher zu speichern (und möglicherweise einige Aktionen auszuführen), und sollte nicht dafür verantwortlich sein, sich in einen anderen, nicht verwandten Typ umzuwandeln .Wahrscheinlich. Ich habe in der Regel Methoden außerhalb der Domäne und DTO-Objekte, um die Konvertierung durchzuführen. Ich verwende oft ein Repository, das Domänenobjekte aufnimmt und sie (de) serialisiert, entweder in eine Datenbank, einen Speicher, eine Datei oder was auch immer. Wenn ich diese Logik in meine Domänenklassen einbaue, habe ich sie jetzt an eine bestimmte Form der Serialisierung gebunden, die für Tests (und andere Dinge) schlecht ist. Wenn ich die Logik in meine DTO-Klassen einbaue, habe ich sie an meine Domäne gebunden, was wiederum das Testen einschränkt.
Nicht im Allgemeinen, obwohl sie können überstrapaziert werden. Mit Erweiterungsmethoden erstellen Sie optionale Erweiterungen, die das Lesen von Code erleichtern. Sie haben Nachteile - es ist einfacher, Mehrdeutigkeiten zu erzeugen, die der Compiler auflösen muss (manchmal unbemerkt), und es kann schwieriger sein, Code zu debuggen, da nicht ganz klar ist, woher die Erweiterungsmethode stammt.
In Ihrem Fall scheint eine Konverter- oder Mapper-Klasse der einfachste und direkteste Ansatz zu sein, obwohl ich nicht glaube, dass Sie mit Erweiterungsmethoden etwas falsch machen .
quelle
Wie wäre es mit AutoMapper oder einem benutzerdefinierten Mapper?
von der Domain
Unter der Haube können Sie AutoMapper abstrahieren oder Ihren eigenen Mapper rollen
quelle
Ich weiß, dass dies alt ist, aber immer noch offen. Auf diese Weise können Sie in beide Richtungen konvertieren. Ich finde dies hilfreich, wenn ich mit Entity Framework arbeite und Viewmodels (DTOs) erstelle.
Ich finde, dass AutoMapper ein bisschen zu viel ist, um wirklich einfache Mapping-Operationen durchzuführen.
quelle