Agregar campo a la clase en tiempo de ejecución - patrón de diseño

15

Imagine que su cliente quiere tener la posibilidad de agregar una nueva propiedad (por ejemplo, color) al producto en su tienda virtual en su CMS.

En lugar de tener propiedades como campos:

class Car extends Product {
   protected String type;
   protected int seats;
}

Probablemente terminarías haciendo algo como:

class Product {
   protected String productName;
   protected Map<String, Property> properties;
}

class Property {
   protected String name;
   protected String value;
}

Es decir, crear un sistema de tipo propio encima del existente. Me parece que esto podría verse como la creación de un lenguaje específico de dominio, ¿o no?

¿Es este enfoque un patrón de diseño conocido? ¿Resolverías el problema de manera diferente? Sé que hay idiomas en los que puedo agregar un campo en tiempo de ejecución, pero ¿qué pasa con la base de datos? ¿Prefieres agregar / modificar columnas o usar algo como se muestra arriba?

Gracias por tu tiempo :).

Filip
fuente
No estoy seguro de qué lenguaje está usando, pero si es C #, podría usar un tipo dinámico que básicamente almacena un KVP en un diccionario similar a lo que está haciendo en los productos y le permite agregar propiedades sin tener que agregarlas directamente a La colección como una colección. Sin embargo, no tendrá una escritura fuerte. Sé que solicitó un patrón de diseño, pero no creo que necesite algo complejo para usarlos. msdn.microsoft.com/en-us/magazine/gg598922.aspx
Tony
Tony: Estoy usando Java aquí, pero considéralo pseudo coude :). ¿C # me permitiría persistir ese objeto dinámico en la base de datos? Lo dudo, ya que databse necesita conocer la estructura de datos por adelantado.
Filip
Existe el patrón de diseño de estado a la banda de cuatro, que hace que un objeto parezca cambiar su tipo o clase en tiempo de ejecución. Otras alternativas son el Patrón de diseño de observador o un Patrón de diseño de proxy
Nikos M.
1
¿Por qué no solo usar el tipo de datos del mapa? En DB puede representarse como {id} + {id, clave, valor}, si no solicita rendimiento.
Shadows In Rain

Respuestas:

4

¡Felicidades! Acaba de dar la vuelta al mundo del lenguaje de programación / sistema de tipos, llegando al otro lado del mundo desde donde partió. ¡Acabas de aterrizar en el borde del lenguaje dinámico / tierra de objetos basados ​​en prototipos!

Muchos lenguajes dinámicos (por ejemplo, JavaScript, PHP, Python) permiten ampliar o cambiar las propiedades de los objetos en tiempo de ejecución.

La forma extrema de esto es un lenguaje basado en prototipos como Self o JavaScript. No tienen clases, estrictamente hablando. Puede hacer cosas que parecen una programación orientada a objetos basada en clases con herencia, pero las reglas son mucho más relajadas en comparación con lenguajes más definidos y basados ​​en clases como Java y C #.

Langauges como PHP y Python viven en el medio. Tienen sistemas regulares, idiomáticos basados ​​en clases. Pero los atributos de objeto se pueden agregar, cambiar o eliminar en tiempo de ejecución, aunque con algunas restricciones (como "excepto para los tipos integrados") que no se encuentran en JavaScript.

La gran compensación para este dinamismo es el rendimiento. Olvídese de cuán fuerte o débilmente tipeado es el idioma, o qué tan bien puede compilarse en código máquina. Los objetos dinámicos deben representarse como mapas / diccionarios flexibles, en lugar de estructuras simples. Esto agrega sobrecarga a cada acceso a objetos. Algunos programas hacen todo lo posible para reducir esta sobrecarga (por ejemplo, con la asignación fantasma kwarg y las clases basadas en slots en Python), pero la sobrecarga adicional generalmente es igual al curso y al precio de admisión.

Volviendo a su diseño, está injertando la capacidad de tener propiedades dinámicas en un subconjunto de sus clases. A Productpuede tener atributos variables; presumiblemente un Invoiceo un Orderpodría y no podría. No es un mal camino a seguir. Le brinda la flexibilidad de tener variaciones donde las necesite, mientras se mantiene en un lenguaje estricto y disciplinado y un sistema de tipos. En el lado negativo, usted es responsable de administrar esas propiedades flexibles, y probablemente tendrá que hacerlo a través de mecanismos que se ven ligeramente diferentes de los atributos más nativos. p.prop('tensile_strength')en lugar de p.tensile_strength, por ejemplo, y en p.set_prop('tensile_strength', 104.4)lugar dep.tensile_strength = 104.4. Pero trabajé y construí muchos programas en Pascal, Ada, C, Java e incluso en lenguajes dinámicos que usaban exactamente ese acceso getter-setter para tipos de atributos no estándar; El enfoque es claramente viable.

Por cierto, esta tensión entre los tipos estáticos y un mundo muy variado es extremadamente común. A menudo se ve un problema análogo al diseñar el esquema de la base de datos, especialmente para los almacenes de datos relacionales y pre-relacionales. A veces se trata creando "super filas" que contienen suficiente flexibilidad para contener o definir la unión de todas las variaciones imaginadas, y luego rellenar cualquier dato que ingrese en esos campos. El WordPress wp_postsmesa , por ejemplo, tiene campos como comment_count, ping_status, post_parenty post_date_gmtque sólo son interesantes en algunas circunstancias, y que en la práctica a menudo queda en blanco. Otro enfoque es una tabla normalizada , muy libre, wp_optionscomo suPropertyclase. Si bien requiere una administración más explícita, los elementos en él rara vez están en blanco. Las bases de datos orientadas a objetos y documentos (por ejemplo, MongoDB) a menudo tienen más facilidad para manejar las opciones cambiantes, porque pueden crear y establecer atributos prácticamente a voluntad.

Jonathan Eunice
fuente
0

Me gusta la pregunta, mis dos centavos:

Sus dos enfoques son radicalmente diferentes:

  • El primero es OO y fuertemente tipado, pero no extensible
  • El segundo tiene un tipo débil (la cadena encapsula cualquier cosa)

En C ++, muchos usarían un std :: map of boost :: variant para lograr una combinación de ambos.

Disgresión: Tenga en cuenta que algunos lenguajes, como C #, permiten la creación dinámica de tipos. Lo que podría ser una buena solución para el problema general de agregar miembros dinámicamente. Sin embargo, "modificar / agregar" tipos después de la compilación corrompe el sistema de tipos en sí mismo y hace que sus tipos "modificados" sean casi inútiles (por ejemplo, ¿cómo accedería a esas propiedades agregadas, ya que ni siquiera sabe que existen? La única forma razonable sería ser un reflejo sistemático sobre cada objeto ... terminando con un lenguaje dinámico puro _ puede referirse a la palabra clave 'dinámica' .NET)

cuantdev
fuente
Crear tipos en tiempo de ejecución parece interesante pero demasiado exótico para mí (estoy programando en Java). Dicha solución no funcionaría si quisiera almacenar objetos en databse, que siempre está fuertemente tipeado, creo. La solución de tipo débil que he propuesto se puede almacenar en la base de datos fácilmente.
Filip
0

Crear un tipo en tiempo de ejecución suena mucho más complicado que simplemente crear una capa de abstracción. Es muy común crear una abstracción para desacoplar sistemas.

Déjame mostrarte un ejemplo de mi práctica. El intercambio de Moscú tiene el núcleo comercial llamado Plaza2 con API de operador. Los comerciantes escriben sus programas para trabajar con datos financieros. El problema es que estos datos son muy grandes, complejos y altamente expuestos a cambios. Puede cambiar después de la introducción de un nuevo producto financiero o cambios en los roles de compensación. La naturaleza de los cambios futuros no se puede predecir. Literalmente, puede cambiar todos los días y los programadores pobres deberían editar el código y lanzar una nueva versión, y los comerciantes enojados deberían revisar sus sistemas.

La decisión obvia es ocultar todo el infierno financiero detrás de una abstracción. Han utilizado abstracciones de tablas SQL bien conocidas. La mayor parte de su núcleo puede funcionar con cualquier esquema válido, al igual que el software del operador puede analizar dinámicamente el esquema y averiguar si es compatible con su sistema.

Volviendo a su ejemplo, es normal crear un lenguaje abstracto frente a alguna lógica. Podría ser "propiedad", "tabla", "mensaje" o incluso lenguaje humano, pero preste atención a los inconvenientes de este enfoque:

  • Más código para analizar y validar (y más tiempo fuera del curso). Todo lo que el compilador hizo por ti con tipeo estático debe hacerlo en tiempo de ejecución.
  • Más documentación de mensajes, tablas u otras primitivas. Toda la complejidad va del código a algún tipo de esquema o estándar. Aquí hay un ejemplo del esquema del infierno financiero mencionado anteriormente: http://ftp.moex.com/pub/FORTS/Plaza2/p2gate_en.pdf (docenas de páginas de tablas)
astef
fuente
0

¿Es este enfoque un patrón de diseño conocido?

En XML y HTML, estos serían los atributos de un nodo / elemento. También los he escuchado denominados propiedades extendidas, pares de nombre / valor y parámetros.

¿Resolverías el problema de manera diferente?

Así es como resolvería el problema, sí.

Sé que hay idiomas en los que puedo agregar un campo en tiempo de ejecución, pero ¿qué pasa con la base de datos?

Una base de datos sería como Java, en algunos sentidos. En pesudo-sql:

TABLE products
(
    product_name VARCHAR(50),
    product_id INTEGER AUTOINCREMENT
)

TABLE attributes
(
    product_id INTEGER,
    name VARCHAR(50),
    value VARCHAR(2000)
)

Esto correspondería al Java

class Product {
   protected String productName;
   protected Map<String, String> properties;
}

Tenga en cuenta que no hay necesidad de una clase de Propiedad, ya que el Mapa almacena el nombre como la clave.

¿Prefieres agregar / modificar columnas o usar algo como se muestra arriba?

He intentado agregar / modificar columna, y fue una pesadilla. Se puede hacer, pero las cosas no se sincronizaban y nunca conseguí que funcionara bien. La estructura de la tabla que describí anteriormente ha sido mucho más exitosa. Si lo que necesita hacer búsquedas y por fin de, considerar el uso de una tabla de atributos para cada tipo de datos ( date_attributes, currency_attributes, etc.) o la adición de algunas de las propiedades como buenos viejos columnas de base de datos en la tabla productos. Los informes son a menudo mucho más fáciles de escribir en columnas de bases de datos que en subtablas.

Guy Schalnat
fuente