Dos estructuras con los mismos miembros pero con nombres diferentes, ¿es una buena idea?

49

Estoy escribiendo un programa que implica trabajar con coordenadas polares y cartesianas.

¿Tiene sentido crear dos estructuras diferentes para cada tipo de puntos, una con los miembros X y Y y la otra con los miembros R y Theta ?

O es demasiado y es mejor tener solo una estructura con first y second como miembros.

Lo que estoy escribiendo es simple y no cambiará mucho. Pero tengo curiosidad por saber qué es lo mejor desde el punto de vista del diseño.

Estoy pensando que la primera opción es mejor. Parece más legible y obtendré el beneficio de la verificación de tipos.

    
pregunta Mhd.Tahawi 10.11.2015 - 09:36

7 respuestas

17

He visto ambas soluciones, por lo que definitivamente depende del contexto.

Para facilitar la lectura, tener varias estructuras como usted sugiere es muy efectivo. Sin embargo, en algunos entornos, desea realizar manipulaciones comunes a estas estructuras y se encuentra duplicando código, como las operaciones matriciales * vectoriales. Puede resultar frustrante cuando la operación en particular no está disponible en su sabor de vector porque nadie la portó allí.

La solución extrema (a la que finalmente cedimos) es tener una clase base con plantilla CRTP con funciones obtener < 0 > () obtener < 1 > () y obtener < 2 > () para obtener los elementos de manera genérica . Esas funciones se definen luego en la estructura cartesiana o polar que se deriva de esta clase base. Resuelve todos los problemas, pero tiene un precio bastante tonto: tener que aprender metaprogramación de plantillas. Sin embargo, si la metaprogramación de plantillas ya es un juego justo para su proyecto, podría ser una buena combinación.

    
respondido por el Cort Ammon 10.11.2015 - 18:18
114

Sí, tiene mucho sentido.

El valor de una estructura no es solo que encapsula datos bajo un nombre práctico. El valor es que codifica sus intenciones para que el compilador pueda ayudarlo a verificar que no las viole algún día (por ejemplo, confunda un conjunto de coordenadas polares con un conjunto de coordenadas cartesianas).

La gente es mala para recordar detalles tan molestos, pero buena para crear planes audaces e inventivos. Las computadoras son buenas en detalles pequeños y malas en los planes creativos. Por lo tanto, siempre es una buena idea cambiar todo el mantenimiento de detalles de la computadora a la computadora, dejando su mente libre para trabajar en el gran plan.

    
respondido por el Kilian Foth 10.11.2015 - 09:40
18

Sí, mientras que tanto los cartesianos como los polares son (en su lugar) esquemas de representación de coordenadas eminentemente sensibles, idealmente nunca deberían mezclarse (si tiene un punto cartesiano {1,1}, es un punto muy diferente del polar { 1,1}).

Dependiendo de sus necesidades, también puede valer la pena implementar una interfaz de Coordenadas, con métodos como X() , Y() , Displacement() y Angle() (o posiblemente Radius() y Theta() , dependiendo de) .

    
respondido por el Vatine 10.11.2015 - 11:15
8

Al final, el objetivo de la programación es cambiar los bits de transistor para realizar un trabajo útil. Pero pensar en un nivel tan bajo llevaría a una locura inmanejable, razón por la cual existen lenguajes de programación de nivel superior para ayudarlo a ocultar la complejidad.

Si solo creas una estructura con miembros llamados first y second , entonces los nombres no significan nada; Esencialmente los tratarías como direcciones de memoria. Eso anula el propósito del lenguaje de programación de alto nivel.

Además, solo porque todos ellos se pueden representar como double no significa que pueda usarlos indistintamente. Por ejemplo, θ es un ángulo sin dimensiones, mientras que y tiene unidades de longitud. Dado que los tipos no son lógicamente sustituibles, deben ser dos estructuras incompatibles.

Si realmente necesita jugar trucos para reutilizar la memoria, y seguramente no debería hacerlo, puede usar un tipo union en C para aclarar su intención.

    
respondido por el 200_success 10.11.2015 - 21:41
2

En primer lugar, tenga ambos de manera explícita, según la respuesta totalmente acertada de @ Kilian-foth.

Sin embargo, me gustaría añadir:

Pregunte: ¿Realmente tiene operaciones que son genéricas para ambos cuando se consideran como pares de double ? Tenga en cuenta que esto no es lo mismo que decir que tiene operaciones que se aplican a ambos en sus propios términos. Por ejemplo, 'plot (Coord)' le importa si el Coord es Polar o Cartesiano. Por otro lado, persistir en el archivo solo trata los datos como están. Si realmente tiene operaciones genéricas, considere definir una clase base o definir un convertidor a std::pair<double, double> o tuple o lo que tenga en su idioma.

Además, un enfoque podría ser tratar un tipo de Coordenadas como más fundamental y el otro como un mero apoyo para el usuario o la interacción externa.

Por lo tanto, puede asegurarse de que todas las operaciones fundamentales estén codificadas para Cartesian y luego brindar soporte para convertir Polar a Cartesian . Esto evita la implementación de diferentes versiones de muchas operaciones.

    
respondido por el Keith 10.11.2015 - 23:34
1

Una posible solución, dependiendo del idioma y si sabe que ambas clases tendrán métodos y operaciones similares, sería definir la clase una vez y usar alias de tipo para nombrar explícitamente los tipos de manera diferente.

Esto también tiene la ventaja de que mientras las clases sean exactamente iguales, puede mantener solo una, pero tan pronto como necesite cambiarlas, no necesita modificar el código usándolas ya que los tipos tienen Ya se ha utilizado de forma distinta.

Otra opción, dependiendo de nuevo del uso de las clases (si necesita polimorfismo y demás) es usar la herencia pública para ambos tipos nuevos, de modo que tengan la misma interfaz pública que el tipo común que ambos representan. Esto también permite la evolución separada de los tipos.

    
respondido por el Svalorzen 11.11.2015 - 12:28
0

Creo que tener los mismos nombres de miembros es una mala idea en este caso, ya que hace que el código sea más propenso a errores.

Imagina el escenario: tienes un par de puntos cartesianos: pntA y pntB. Luego, por alguna razón, decide que deberían representarse mejor en coordenadas polares y cambiar la declaración y el constructor.

Ahora, si todas sus operaciones fueran solo llamadas de método como:

double distance = pntA.distanceFrom(pntB);

entonces estás bien. ¿Pero qué pasa si usaste los miembros explícitamente? Comparar

double leftMargin = abs(pntA.x - pntB.x);
double leftMargin = abs(pntA.first - pntB.first);

En el primer caso, el código no se compilará. Verás el error inmediatamente y podrás solucionarlo. Pero si tiene los mismos nombres de miembro, el error será solo en el nivel lógico, mucho más difícil de detectar.

Si escribe en un lenguaje no orientado a objetos, entonces es aún más fácil pasar una estructura incorrecta a la función. ¿Qué te impide escribir el siguiente código?

double distance = calculate_distance_polar(cartesianPointA, polarPointB);

Los diferentes tipos de datos, por otro lado, le permitirían encontrar el error durante la compilación.

    
respondido por el IMil 11.11.2015 - 16:54

Lea otras preguntas en las etiquetas