Em qualquer linguagem de programação existem geradores de números pseudo-aleatórios. Estes geradores são chamados assim pois os números não são de fato aleatórios, uma vez que é utilizada uma equação para calcular cada número gerado. Em Java, a classe Random do pacote java.util é uma das formas utilizadas para gerar tais números. No entanto, existem diferentes implementações destes geradores, cada uma com finalidades específicas ou diferentes níveis de qualidades em relação à aleatoriedade dos números gerados.

Bibliotecas como a Apache Commons Math fornecem, dentre outras funcionalidades, diversos geradores de números pseudo-aleatórios.

1. O problema

Existe uma grande variedade de implementações de tais geradores, cada implementação fornecendo uma interface diferente da outra. Para este projeto, observe o diagrama de classes abaixo.

random numbers

Ele apresenta alguns métodos da classe Random do pacote java.util e da interface RealDistribution da biblioteca Apache Commons Math. Algumas das classes que implementam esta última interface são NormalDistribution e LogNormalDistribution. Como podem ver, existem diferenças entre as classes/interfaces, tornando também diferente a forma de utilizar cada uma das implementações disponíveis.

A seguir são apresentados os detalhes das diferentes implementações.

1.1. Classe Random da plataforma Java

Na classe Random temos os métodos nextDouble() e doubles(long streamSize). Os dois podem ser utilizados para gerar números aleatórios. O primeiro gera um único número a cada vez que for chamado. O segundo gera um conjunto de números, cuja quantidade é definida pelo parâmetro streamSize. Assim, chamando doubles(3), serão gerados 3 números aleatórios.

Adicionalmente, esta classe possui 2 construtores: um que não recebe parâmetro algum e outro que recebe uma seed. A seed (semente) é apenas um número que define o valor inicial do gerador. Se instanciarmos um gerador com a mesma seed, a sequência de números gerados será sempre a mesma. Se alterarmos a seed ou não informarmos uma, a cada vez que executarmos a aplicação, a sequência de números gerados será diferente.

1.2. Biblioteca Apache Commons Math

Já nas classes que implementam a interface RealDistribution da biblioteca Apache Commons Math (como NormalDistribution e LogNormalDistribution), os métodos que retornam um número aleatório ou um conjunto de números são diferentes. Temos os métodos sample() e sample(int sampleSize), semelhantes aos métodos da classe Random, mas com nomes, tipos de parâmetro e retorno diferentes.

Por fim, nestas classes, se for preciso definir uma seed, isto não é feito por meio de um construtor, mas chamando o método reseedRandomGenerator(long seed).

2. O Desafio

Identifique qual padrão de projeto pode ser aplicado para uniformizar a interface das classes Random do pacote java.util e das classes que implementam a interface RealDistribution da biblioteca Apache Commons Math. O padrão deve então permitir que a forma de utilizar qualquer destas classes seja a mesma.

Altere a aplicação de exemplo nesta pasta (que já inclui a dependência para a biblioteca Apache Commons Math no arquivo pom.xml) para aplicar o padrão de projeto adequado. Por fim, modifique a classe Principal para utilizar as classes criadas para o padrão.

3. Detalhes de Implementação

Observe que os métodos sample(int sampleSize) da interface RealDistribution e doubles(long streamSize) da classe Random tem tipos de parâmetros e retorno diferentes. O primeiro retorna um vetor de double e o segundo retorna um objeto DoubleStream. Uma maneira de uniformizar tais métodos é fazer com que o segundo também retorne um vetor de double. O código para isto pode ser:

double[] numeros = random.doubles(10).toArray();

Neste exemplo, estamos chamando o método doubles(), indicando que desejamos gerar 10 números aleatórios e convertendo de DoubleStream para um vetor usando o método toArray().

Estamos padronizando fazendo os métodos retornarem vetores, pelo fato destes serem mais familiares que Streams (como é o caso do DoubleStream). Streams são mais modernos, seguros e flexíveis que vetores, mas vamos utilizar apenas vetores para simplificar.