In dem Projekt, an dem ich arbeite, senden wir Nachrichten über Widgets über Nachrichtenwarteschlangen und serialisieren sie als XML in die Warteschlangen. Das XML-Schema enthält Tags für Eigenschaften, die allen Typen dieser Widgetnachrichten gemeinsam sind, z. B. Widgettyp, Befehlsname und Ziel. Es kann auch eine beliebig große Liste von Schlüssel-Wert-Paaren enthalten, um die Speicherung von Eigenschaften zu ermöglichen, die nur für einen bestimmten Typ von Widget-Nachrichten relevant sind. Die WidgetMessage
Klasse kapselt diese Daten und die Klassen WidgetMessageXmlWriter
und WidgetMessageXmlReader
bieten eine Serialisierung zu und von XML.
Ich habe einige Klassen erstellt, die bestimmte Nachrichten kapseln, z. B. ein FooPlaySoundMessage
Widget für "Foo" oder ein Widget BarSetLightPatternMessage
für "Bar". Sie verfügen jeweils über eine ToWidgetMessage
Instanzmethode und eine FromWidgetMessage
statische Methode zum Konvertieren in und aus der WidgetMessage
Klasse. Jede Nachrichtenfamilie erbt von einer abstrakten Klasse für diesen Widget-Typ, z. B. FooMessage
und BarMessage
, die wiederum von der WidgetMessageMapping
Klasse erbt . Hier werden die allgemeinen Nachrichteneigenschaften und die geschützten Methoden gespeichert, die von Unterklassen für die Konvertierung verwendet werden. Keine dieser Klassen erbt von, WidgetMessage
da ich nicht möchte, dass sie die Schlüsselwertsammlungseigenschaft und die zugehörigen Methoden erben , weshalb eher eine Konvertierung als ein einfaches Casting erforderlich ist.
Ich mag die Einfachheit meiner API (z. B. FooPlaySoundMessage msg = FooPlaySoundMessage.fromWidgetMessage(widgetMessage)
), aber die Tatsache, dass ich geschützte Methoden in einer Basisklasse verwenden muss, um Funktionen gemeinsam zu nutzen, und statische Methoden, um sie verfügbar zu machen, lässt mich fragen, ob es eine oder zwei separate Klassen geben sollte hier (ähnlich WidgetMessageXmlWriter
und WidgetMessageXmlReader
). Andererseits dachte ich, dass ein Teil des Punktes von OOP darin besteht, Daten und Methoden zu gruppieren und so "dumme Datenobjekte" zu vermeiden .
Habe ich also die richtige Idee, indem ich meinen Datenobjekten Konvertierungsmethoden hinzufüge, oder sollte diese Funktionalität in eine andere Klasse extrahiert werden?
Aktualisieren:
Ich denke, dass ich in allen Details meines aktuellen Entwurfsversuchs das Problem, das ich zu lösen versuche, nicht klar genug erklärt habe.
Zusammenfassend habe ich eine "generische" DTO-Klasse mit einigen stark typisierten Eigenschaften und einer Sammlung von Schlüssel-Wert-Paaren zum Speichern anderer benutzerdefinierter Daten. Ich möchte einige spezialisierte DTO-Klassen für jeden Satz von benutzerdefinierten Daten haben, die dieselben Daten wie das generische DTO speichern, außer dass die Schlüssel-Wert-Paare durch stark typisierte Eigenschaften ersetzt werden. Was ist das beste Design für die Konvertierung zwischen diesen beiden DTO-Typen?
quelle
Antworten:
Wenn FooPlaySoundMessage weiß, wie man Sound spielt und wie man sich einem Nachrichtenwarteschlangenformat zuordnet, kann man sagen, dass die Klasse mehr als eine Verantwortung hat. Wenn der eigentliche Sound direkt an eine andere Klasse delegiert wird, ist Ihre FooPlaySoundMessage im Grunde ein Datenübertragungsobjekt. In diesem Fall scheint es mir in Ordnung zu sein, allgemeine Zuordnungen in eine gemeinsam genutzte Basisklasse einzufügen.
Ich würde es wahrscheinlich aber trennen. Datenübertragungsobjekte haben normalerweise wenig bis gar keinen Code. Sie können FooPlaySoundMessage als einen sehen.
Irgendwann müssen Sie möglicherweise dieselben Daten auf andere Weise oder in einem anderen Format übertragen (json vielleicht?). Sie könnten es für jetzt YAGNI und dann trennen.
Ich würde wahrscheinlich eine Message-Handler-Klasse erstellen, die die allgemeinen Teile analysiert, den Nachrichtentyp erkennt und dann an einen bestimmten Parser delegiert, z. B. FooPlaySoundXmlMessageParser. Wenn Sie Zweifel haben, bevorzugen Sie die Komposition gegenüber der Vererbung.
quelle