Considere una situación en la que una clase implementa el mismo comportamiento básico, métodos, etc., pero podrían existir múltiples versiones diferentes de esa clase para diferentes usos. En mi caso particular, tengo un vector (un vector geométrico, no una lista) y ese vector podría aplicarse a cualquier espacio euclidiano N-dimensional (1 dimensional, 2 dimensional, ...). ¿Cómo se puede definir esta clase / tipo?
Esto sería fácil en C ++, donde las plantillas de clase pueden tener valores reales como parámetros, pero no tenemos ese lujo en Java.
Los dos enfoques que se me ocurren para resolver este problema son:
Tener una implementación de cada caso posible en tiempo de compilación.
public interface Vector { public double magnitude(); } public class Vector1 implements Vector { public final double x; public Vector1(double x) { this.x = x; } @Override public double magnitude() { return x; } public double getX() { return x; } } public class Vector2 implements Vector { public final double x, y; public Vector2(double x, double y) { this.x = x; this.y = y; } @Override public double magnitude() { return Math.sqrt(x * x + y * y); } public double getX() { return x; } public double getY() { return y; } }
Obviamente, esta solución consume mucho tiempo y es extremadamente tediosa para el código. En este ejemplo, no parece tan malo, pero en mi código real estoy tratando con vectores que tienen múltiples implementaciones cada uno, con hasta cuatro dimensiones (x, y, zyw). Actualmente tengo más de 2.000 líneas de código, aunque cada vector realmente solo necesita 500.
Especificar parámetros en tiempo de ejecución.
public class Vector { private final double[] components; public Vector(double[] components) { this.components = components; } public int dimensions() { return components.length; } public double magnitude() { double sum = 0; for (double component : components) { sum += component * component; } return Math.sqrt(sum); } public double getComponent(int index) { return components[index]; } }
Desafortunadamente, esta solución perjudica el rendimiento del código, da como resultado un código más desordenado que la solución anterior y no es tan seguro en el momento de la compilación (no se puede garantizar en el momento de la compilación que el vector con el que está tratando sea realmente bidimensional, por ejemplo).
Actualmente estoy desarrollando en Xtend, por lo que si hay soluciones Xtend disponibles, también serían aceptables.
fuente
Respuestas:
En casos como este, uso la generación de código.
Escribo una aplicación Java que genera el código real. De esa manera, puede usar fácilmente un bucle for para generar un montón de versiones diferentes. Yo uso JavaPoet , lo que hace que sea bastante sencillo construir el código real. Luego puede integrar la ejecución de la generación de código en su sistema de compilación.
fuente
Tengo un modelo muy similar en mi aplicación y nuestra solución fue simplemente mantener un mapa de un tamaño dinámico, similar a su solución 2.
Simplemente no tendrá que preocuparse por el rendimiento con un primitivo java array como ese. Generamos matrices con tamaños de límite superior de 100 columnas (léase: 100 vectores dimensionales) por 10.000 filas, y hemos tenido un buen rendimiento con tipos de vectores mucho más complejos que su solución 2. Puede intentar sellar la clase o los métodos de marcado como final acelerarlo, pero creo que estás optimizando prematuramente.
Puede obtener algunos ahorros en el código (a costa del rendimiento) creando una clase base para compartir su código:
Entonces, por supuesto, si está en Java-8 +, puede usar interfaces predeterminadas para hacer esto aún más estricto:
En última instancia, más allá de eso, no tiene opciones con la JVM. Por supuesto, puede escribirlos en C ++ y usar algo como JNA para unirlos; esta es nuestra solución para algunas de las operaciones de matriz rápida, donde usamos fortran e MKL de inteligencia, pero esto solo ralentizará las cosas si simplemente escribe tu matriz en C ++ y llama a sus captadores / establecedores desde java.
fuente
data class
objetos para crear fácilmente 10 subclases de vectores. Con Java, suponiendo que pueda incorporar toda su funcionalidad a la clase base, cada subclase tomará entre 1 y 10 líneas. ¿Por qué no crear una clase base?asArray
método, esos diversos métodos no se verificarían en el momento de la compilación (podría realizar un producto punto entre un vector escalar y un vector cartesiano y se compilaría bien, pero fallará en el tiempo de ejecución) .Considere una enumeración con cada Vector nombrado que tenga un constructor que consista en una matriz (inicializada en la lista de parámetros con los nombres de dimensión o similar, o tal vez solo un número entero para el tamaño o una matriz de componentes vacía, su diseño) y una lambda para El método getMagnitude. Podría hacer que la enumeración también implemente una interfaz para setComponents / getComponent (s), y simplemente establezca qué componente era cuál en su uso, eliminando getX, et al. Debería inicializar cada objeto con sus valores de componentes reales antes de su uso, posiblemente comprobando que el tamaño de la matriz de entrada coincida con los nombres o el tamaño de la dimensión.
Luego, si extiende la solución a otra dimensión, simplemente modifica la enumeración y lambda.
fuente
Según su opción 2, ¿por qué no simplemente hacer esto? Si desea evitar el uso de la base en bruto, puede hacerla abstracta:
fuente
double[]
archivo no es deseable en comparación con una implementación que simplemente usa 2 primitivasdouble
. En un ejemplo tan mínimo como este, parece una microoptimización, pero considere un caso mucho más complejo en el que intervienen muchos más metadatos y el tipo en cuestión tiene una vida útil corta.Vector
con una implementación más especializada (por ejemploVector3
) si su vida útil fuera relativamente larga.Una idea:
Esto le brinda un buen rendimiento en casos típicos y algo de seguridad en tiempo de compilación (aún se puede mejorar) sin sacrificar el caso general.
Esqueleto de código:
fuente