Estoy escribiendo un programa que implica trabajar con coordenadas polares y cartesianas.
¿Tiene sentido crear dos estructuras diferentes para cada tipo de puntos, uno con X
y Y
miembros y otro con R
y Theta
miembros.
¿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 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.
Respuestas:
He visto ambas soluciones, por lo que definitivamente depende del contexto.
Para facilitar la lectura, tener múltiples estructuras como sugiere es muy efectivo. Sin embargo, en algunos entornos, desea realizar manipulaciones comunes a estas estructuras, y se encuentra duplicando el código, como las operaciones de vector de matriz *. Puede ser frustrante cuando la operación particular no está disponible en su sabor de vector porque nadie lo portó allí.
La solución extrema (a la que finalmente nos rendimos) es tener una clase base con plantilla CRTP con funciones get <0> () get <1> () y get <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 la 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.
fuente
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).
Las personas son malas para recordar detalles tan insignificantes, pero buenos para crear planes atrevidos e ingeniosos. Las computadoras son buenas en detalles insignificantes y malas en planes creativos. Por lo tanto, siempre es una buena idea trasladar la mayor cantidad de mantenimiento a la computadora, dejando su mente libre para trabajar en el gran plan.
fuente
Sí, aunque tanto Cartesian como Polar son (en su lugar) esquemas de representación de coordenadas eminentemente sensibles, idealmente nunca deberían mezclarse (si tiene un punto Cartesian {1,1}, es un punto muy diferente de Polar {1,1 }).
Dependiendo de sus necesidades, sino que también puede valer la pena implementar una interfaz de coordenadas, con métodos como
X()
,Y()
,Displacement()
yAngle()
(o, posiblemente,Radius()
yTheta()
, dependiendo).fuente
Al final, el objetivo de la programación es alternar los bits del transistor para hacer un trabajo útil. Pero pensar a un nivel tan bajo conduciría a una locura inmanejable, por lo que existen lenguajes de programación de alto nivel para ayudarlo a ocultar la complejidad.
Si solo crea una estructura con miembros nombrados
first
ysecond
, entonces los nombres no significan nada; esencialmente los trataría como direcciones de memoria. Eso anula el propósito del lenguaje de programación de alto nivel.Además, el hecho de que todos sean representables como un
double
no significa que puede usarlos indistintamente. Por ejemplo, θ es un ángulo adimensional, mientras que y tiene unidades de longitud. Como los tipos no son lógicamente sustituibles, deberían ser dos estructuras incompatibles.Si realmente necesita jugar trucos de reutilización de memoria, y seguramente no debería, puede usar un
union
tipo en C para aclarar su intención.fuente
En primer lugar, tenga ambos explícitamente, según la respuesta completamente sólida de @ Kilian-foth.
Sin embargo, me gustaría agregar:
Pregunte: ¿Realmente tienen operaciones que son genéricas para ambos cuando se consideran 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 siCoord
es polar o cartesiano. Por otro lado, persistir en el archivo solo trata los datos como son. Si realmente tiene operaciones genéricas, considere la posibilidad de definir, ya sea una clase base, o la definición de un convertidor a unastd::pair<double, double>
otuple
o lo que usted tiene en su idioma.Además, un enfoque podría ser tratar un tipo de Coordenada como más fundamental y el otro simplemente como soporte para la interacción externa o del usuario.
Por lo tanto, puede asegurarse de que todas las operaciones fundamentales estén codificadas
Cartesian
y luego brindar soporte para la conversiónPolar
aCartesian
. Esto evita implementar diferentes versiones de muchas operaciones.fuente
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, siempre y cuando las clases sean exactamente iguales, puede mantener solo una, pero tan pronto como necesite cambiarlas, no necesita modificar el código que las usa, ya que los tipos ya se han utilizado. distintamente
Otra opción, dependiendo nuevamente del uso de las clases (si necesita polimorfismo y tal) 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 una evolución separada de los tipos.
fuente
Creo que tener los mismos nombres de miembros es una mala idea en este caso, porque hace que el código sea más propenso a errores.
Imagine el escenario: tiene un par de puntos cartesianos: pntA y pntB. Luego decide, por alguna razón, que deberían estar mejor representados en coordenadas polares, y cambiar la declaración y el constructor.
Ahora, si todas sus operaciones fueran solo llamadas a métodos como:
Entonces estás bien. Pero, ¿qué pasa si usaste los miembros explícitamente? Comparar
En el primer caso, el código no se compilará. Verá el error inmediatamente y podrá solucionarlo. Pero si tiene los mismos nombres de miembros, 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, es aún más fácil pasar una estructura incorrecta a la función. ¿Qué te impide escribir el siguiente código?
Los diferentes tipos de datos, por otro lado, le permitirán encontrar el error durante la compilación.
fuente