Considere uma situação em que uma classe implemente o mesmo comportamento básico, métodos etc., mas várias versões diferentes dessa classe podem existir para diferentes usos. No meu caso particular, eu tenho um vetor (um vetor geométrico, não uma lista) e esse vetor pode ser aplicado a qualquer espaço euclidiano N-dimensional (1 dimensional, 2 dimensional, ...). Como essa classe / tipo pode ser definida?
Isso seria fácil em C ++, onde os modelos de classe podem ter valores reais como parâmetros, mas não temos esse luxo em Java.
As duas abordagens que posso pensar para resolver esse problema são:
Ter uma implementação de cada caso possível em tempo de compilação.
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; } }
Esta solução é obviamente muito demorada e extremamente tediosa para codificar. Neste exemplo, não parece tão ruim, mas no meu código real, estou lidando com vetores que têm várias implementações cada, com até quatro dimensões (x, y, z ew). Atualmente, tenho mais de 2.000 linhas de código, embora cada vetor precise realmente de 500.
Especificando parâmetros em tempo de execução.
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]; } }
Infelizmente, essa solução prejudica o desempenho do código, resulta em um código mais confuso do que a solução anterior e não é tão segura no tempo de compilação (não é possível garantir no tempo de compilação que o vetor com o qual você está lidando é bidimensional, por exemplo).
No momento, estou atualmente desenvolvendo no Xtend, portanto, se houver alguma solução disponível no Xtend, elas também serão aceitáveis.
fonte
Respostas:
Em casos como esse, eu uso geração de código.
Eu escrevo um aplicativo java que gera o código real. Dessa forma, você pode facilmente usar um loop for para gerar várias versões diferentes. Eu uso o JavaPoet , o que torna bastante simples a criação do código real. Em seguida, você pode integrar a execução da geração de código ao seu sistema de construção.
fonte
Eu tenho um modelo muito semelhante no meu aplicativo e nossa solução foi simplesmente manter um mapa de tamanho dinâmico, semelhante à sua solução 2.
Você simplesmente não precisará se preocupar com o desempenho com uma matriz java primitiva como essa. Geramos matrizes com tamanhos de limite superior de 100 colunas (leia-se: 100 vetores dimensionais) por 10.000 linhas e tivemos um bom desempenho com tipos de vetores muito mais complexos que a sua solução 2. Você pode tentar selar os métodos de classe ou marcação como final. para acelerar, mas acho que você está otimizando prematuramente.
Você pode obter algumas economias de código (ao custo do desempenho) criando uma classe base para compartilhar seu código:
Então, é claro, se você estiver no Java-8 +, poderá usar interfaces padrão para tornar isso ainda mais difícil:
Em última análise, além disso, você está sem opções com a JVM. É claro que você pode escrevê-los em C ++ e usar algo como JNA para conectá-los - esta é a nossa solução para algumas das operações de matriz rápida, onde usamos o fortran e o MKL da intel-- mas isso só vai desacelerar as coisas se você simplesmente escreve sua matriz em C ++ e chama seus getters / setters de java.
fonte
data class
objetos para criar facilmente 10 subclasses de vetores. Com o java, supondo que você possa puxar toda a sua funcionalidade para a classe base, cada subclasse terá de 1 a 10 linhas. Por que não criar uma classe base?asArray
método, esses vários métodos não seriam verificados em tempo de compilação (você pode executar um produto escalar entre um vetor escalar e um vetor cartesiano e ele compilará bem, mas falhará em tempo de execução) .Considere uma enumeração com cada vetor nomeado com um construtor que consiste em uma matriz (inicializada na lista de parâmetros com os nomes de dimensão ou similar, ou talvez apenas um número inteiro para o tamanho ou uma matriz de componentes vazios - seu design) e uma lambda para o método getMagnitude. Você poderia fazer com que o enum também implementasse uma interface para setComponents / getComponent (s) e apenas estabelecesse qual componente era qual em seu uso, eliminando getX, et al. Você precisaria inicializar cada objeto com seus valores reais de componentes antes de usar, possivelmente verificando se o tamanho da matriz de entrada corresponde aos nomes ou tamanho da dimensão.
Então, se você estender a solução para outra dimensão, basta modificar o enum e o lambda.
fonte
Com base na sua opção 2, por que não fazer isso simplesmente? Se você deseja impedir o uso da base bruta, você pode torná-la abstrata:
fonte
double[]
é indesejável em comparação com uma implementação que simplesmente usa 2 primitivosdouble
. Em um exemplo tão mínimo quanto isso, parece uma microoptimização, mas considere um caso muito mais complexo em que muito mais metadados estão envolvidos e o tipo em questão tem uma vida útil curta.Vector
por uma implementação mais especializada (por exemploVector3
) se sua vida útil fosse relativamente longa.Uma ideia:
Isso fornece bom desempenho em casos típicos e alguma segurança em tempo de compilação (ainda pode ser aprimorada) sem sacrificar o caso geral.
Esqueleto do código:
fonte