¿Qué diseño de API para almacenar datos genéricos en un formato más específico?

8

En el proyecto en el que estoy trabajando, enviamos mensajes sobre widgets a través de colas de mensajes, serializándolos en las colas como XML. El esquema XML contiene etiquetas para propiedades que son comunes a todos los tipos de estos mensajes de widget, como el tipo de widget, el nombre del comando y el destino. También puede contener una lista de pares de valores clave de tamaño arbitrario para permitir el almacenamiento de propiedades que solo son relevantes para un tipo específico de mensaje de widget. La WidgetMessageclase encapsula estos datos y las clases WidgetMessageXmlWritery WidgetMessageXmlReaderproporcionan serialización hacia y desde el XML.

He realizado algunas clases que encapsulan mensajes específicos, por ejemplo, un FooPlaySoundMessagewidget 'Foo' o un widget BarSetLightPatternMessage'Bar'. Cada uno tiene un ToWidgetMessagemétodo de instancia y un FromWidgetMessagemétodo estático para convertir ay desde la WidgetMessageclase. Cada familia de mensajes hereda de una clase abstracta para ese tipo de widget, por ejemplo , FooMessagey BarMessage, que a su vez hereda de la WidgetMessageMappingclase; Esto almacena las propiedades comunes de los mensajes y los métodos protegidos utilizados por las subclases para la conversión. Ninguna de estas clases hereda WidgetMessageya que no quiero que hereden su propiedad de colección de valores clave y los métodos asociados , de ahí la necesidad de conversión en lugar de simple conversión.

Me gusta la simplicidad de mi API (por ejemplo FooPlaySoundMessage msg = FooPlaySoundMessage.fromWidgetMessage(widgetMessage)), pero el hecho de que tenga que usar métodos protegidos en una clase base para compartir funcionalidad y métodos estáticos para exponerla, me hace preguntarme si debería haber una o dos clases separadas involucradas aquí (similar a WidgetMessageXmlWritery WidgetMessageXmlReader). Por otro lado, pensé que parte del objetivo de OOP es agrupar datos y métodos juntos y así evitar "objetos de datos tontos" .

Entonces, ¿tengo la idea correcta al agregar métodos de conversión a mis objetos de datos, o esa funcionalidad debería extraerse en otra clase?

Actualizar:

Creo que en todos los detalles anteriores de mi intento actual de diseño no expliqué con suficiente claridad el problema que estoy tratando de resolver.

En resumen, tengo una clase DTO "genérica" ​​que tiene algunas propiedades fuertemente tipadas y una colección de pares clave-valor para almacenar otros datos personalizados. Quiero tener algunas clases de DTO especializadas para cada conjunto de datos personalizados que almacenen todos los mismos datos que el DTO genérico, excepto con los pares clave-valor reemplazados por propiedades fuertemente tipadas. ¿Cuál es el mejor diseño para convertir entre estos dos tipos de DTO?

Robert Johnson
fuente
¿Suena como el patrón Decorador? - en.wikipedia.org/wiki/Decorator_pattern
Stevo
En segundo lugar eso. Creo que estas bien.
Stu
1
Además, como una aleatoriedad al margen: si funciona para ti, está dentro.
Stu
¿Qué hace el mensaje en lugar de ser solo el mensaje?
Piotr Gwiazda
La responsabilidad exclusiva de estas clases es convertirse desde y hacia la clase WidgetMessage. Hay un proyecto de 'Widget Comms' que consume WidgetMessages y habla con los widgets; No he agregado dicha funcionalidad de comunicación a estas clases de WidgetMessageMapping porque solo el proyecto Widget Comms debería saber cómo comunicarse con los widgets, mientras que los mensajes se pueden crear en varias partes diferentes de la aplicación. Los WidgetMessageMappings están ahí simplemente para evitar que el análisis de mensajes y el código de creación se extiendan por las capas de la aplicación.
Robert Johnson

Respuestas:

1

Si FooPlaySoundMessage sabe cómo reproducir sonido y también cómo mapearse a sí mismo en un formato de cola de mensajes, podría decir que la clase tiene más de una responsabilidad. Si delega directamente el sonido real que se reproduce en una clase diferente, su FooPlaySoundMessage es básicamente un objeto de transferencia de datos. Poner asignaciones comunes en una clase base compartida me parece bien en ese caso.

Sin embargo, probablemente lo separaría. Los objetos de transferencia de datos generalmente tienen poco o ningún código, puede ver FooPlaySoundMessage como uno.

En algún momento, es posible que tenga que transmitir los mismos datos por algún otro medio u otro formato (¿quizás Json?). Podrías YAGNI por ahora y separarlo entonces.

Probablemente haría una clase de controlador de mensajes que analice las partes comunes, detecte el tipo de mensaje y luego delegue a un analizador específico, por ejemplo, FooPlaySoundXmlMessageParser. En caso de duda, favorezca la composición sobre la herencia.

Joppe
fuente
Es solo el WidgetMessage el que se convierte ay desde XML; los DTO especializados solo se convertirán desde y hacia WidgetMessages. En mi diseño actual todavía se llaman 'mensajes', a pesar de que es solo el WidgetMessage el que se pone en la cola, por lo que puedo entender su confusión. He actualizado mi pregunta para explicar mejor el problema que estoy tratando de resolver.
Robert Johnson