MeasureRuler

De Wiki do Leitão
Ir para: navegação, pesquisa

O MeasureRuler é, pela própria tradução, uma régua de medidas. Essa régua de medidas tem a intenção de oferecer aos sistemas a facilidade para converter unidades de medidas da mesma dimensão, ou até mesmo entre dimensões diferentes quando estabelecida a regra de conversão.

Definições:

  • Dimensão - tipo de medida utiliza, como "Comprimento", "Área", "Volume", "Massa (Peso)", etc. Embora estas hoje sejam as dimensões que o BIS suporta, nada impede de se criar dimensões menos usuais como "Energia", "Luminosidade", "Torque", etc..
  • Unidade de Medida - Unidade de medida utilizada para medir a Dimensão desejada. Por exemplo, comprimento pode ser medido em "Metros", "Centimetros", "Pés", "Polegadas", etc. "Massa" pode ser medida em "Arrobas", "Kilos", "Pounds", etc.

A classe MeasureRuler apresenta diversos métodos para conversão de forma. Para mais informações sobre eles consulte o JavaDoc da classe.


MeasureUnit

O MeasureRuler suporta várias dimensões de medidas. Cada dimensão pode conter várias unidades de medidas. A dimensão é definida pela enumeration MeasureDimension. Já as unidades de cada unidade de medida são definidas pela sua própria enumeration que estendem a interface MeasureUnit. Por exemplo:

  • Volume - VolumeUnit
  • Comprimento - LengthUnit
  • Área - AreaUnit
  • Massa (peso) - WeightUnit
  • Unidades - UnitUnit


Interface MeasureUnit

Cada dimensão tem suas unidades de medidas em enumerations próprias. Assim, quando precisamos representar qualquer uma das unidades de medida utilizamos essa interface. Um exemplo: ao cadastrar um produto no sistema, a "unidade básica de estoque" dele pode ser tanto Unidades, quando Metros, Kilos, Metros Quadrados, etc.. Assim, a melhor maneira de definir este atributo no VO é através desta interface.

A interface MeasureUnit apresenta métodos como getDimension(), que retorna a enumeration de MeasureDimension para indicar que Dimensão de Medida esta Unidade pertence.


Persistência da Interface MeasureUnit

MeasureUnitDAOConverter (RFWDAO)

Por tratar-se de uma enumeration, o padrão do FrameWork é que no banco seja um campo do tipo varchar(50) (O limite recomendado de enums no RFW é de 50 chars). Mas, uma vez que a MeasureUnit é uma interface, o RFWDAO não sabe como proceder para persisti-la e reconstruir o VO com os dados do banco. Para isso temos de utilizar uma RFWDAOConverter. E para simplificar, o MeasureRuler já oferece uma implementação pronta para isso a MeasureUnitDAOConverter.


Note 64.png
Classe Disponível no RFW.DAO
Apesar do Measure ser disponibilizado no RFW.Kernel, as classes de persistência e conversão estão disponibilizadas apenas no módulo RFW.DAO, afinal essas classes só fazem sentido em conjunto com o RFW.


Para definir um atributo no VO que seja do tipo MeasureUnit devemos ter algo como:

Java 256.png Exemplos de Declaração
@BISDAOConverter(converterClass = MeasureUnitDAOConverter.class)
@BISMetaGenericField(caption = "Unidade de Medida", required = true)
private MeasureUnit measureUnit = null;


Sempre que o RFWDAO ler a String no banco de dados que representa a unidade de medida, o Converter conseguirá descobrir de qual enumeration devemos realizar o "valueOf(...)".


Persistindo sem o RFWDAO

Ao persistir o RFWVO sem o RFWDAO, será necessário identificar como o seu sistema salva as enumerações. Embora existam diversas maneiras de se resolver, se seguir o método do RFWDAO, em que as enumerations são salvas somente pelos seus nomes, ao recuperar o nome do banco de dados e utilizar o ".valueOf()" a operação falhará, já que não sabemos de qual enumeration a unidade pertence.

Para resolver isso o MeasureRuler tem o seguinte método:

MeasureRuler.valueOf(...);

Este método identifica a unidade pelo nome, entre todas as dimensões existentes no MeasureRuler.


Stop 256.png
Unidades de Medidas Iguais
Por conta de termos várias unidades de medidas de dimensões diferentes representadas em enumerations diferentes, o Java permitirá que tenhamos uma unidade de medida "X" tanto em VolumeUnit quanto em AreaUnit. Embora o Java não tenha problemas para compilar esse código, ao ser persistido no banco de dados (salvo apenas o NAME da enumeration), a recuperação do objeto é baseada apenas no nome da enumeration. Isso fará com que seja sempre retornada pela primeira enumeration que o código procura.

Para evitar esse problemas as unidades de medidas devem sempre ter nomes diferentes mesmo que em dimensões distintas. Embora não conheça nenhuma unidade de medida que tenha o mesmo o nome e represente coisas diferentes, é bom que o DEV tenha ciência dessa situação.

Utilização da Régua

Para realizar a conversão das unidades de medidas devemos focar nos métodos .convertTo(...) da classe MeasureRuler. Há métodos simples utilizados para conversão dentro da mesma dimensão (sem utilização de equivalências) até métodos completos com a entrada de equivalências e definição da precisão de casas decimais.

Exemplos de Utilização

Veja alguns exemplos de utilização da MeasureRuler:

Java 256.png Conversão de Unidade de Medida dentro da Mesma Dimensão
  // Converte 1000ml em Litros, retornará um BigDecimal com o valor de 1.000
  MeasureRuler.convertTo(new BigDecimal("1000"), VolumeUnit.MILLILITER, VolumeUnit.LITER);

  // Converte 1000ml em Litros, mas dessa vez retornará com a precisão de 6 casas decimais.
  MeasureRuler.convertTo(new BigDecimal("1000"), VolumeUnit.MILLILITER, VolumeUnit.LITER, 6);

  //  Converte 1000g em Kilogramas, com uma precisão de 6 casas decimais, retornará o valor 1.000000
  MeasureRuler.convertTo(new BigDecimal("1000"), WeightUnit.GRAM, WeightUnit.KILOGRAM, 6);


Para converter entre diferentes dimensões (grandezas de medidas diferentes), vamos primeiro estabelecer uma regra de equivalência entre as grandezas, podendo ser feita com qualquer unidade de medida da grandeza.

Vejamos um exemplo, imagine um sistema de marcenaria que precise cadastrar uma chapa de madeira utilizada para móveis. No estoque e durante o processo de compra, referenciamos o item por unidades, pois fica mais fácil de fazer o pedido e comparar preços. Mas no momento de calcular o frete, precisamos saber qual o peso de cada chapa, ou qual o peso final do móvel produzido, de acordo com a "quantidade de chapa" (medida em metros quadrados (área)) que foi recortado e utilizado. Para ter todas essas informações precisamos estabelecer a relação entre as grandezas Unidades, Massa (Peso) e Área.

Imagine que após verificar o produto, descobrimos que a chama tem medida de 2m x 1,8m (=3,6m²), e que ela pesa 76Kg. Assim, montamos nossos dados de equivalência:


Java 256.png Criando Regras de Equivalência
  // Regras de Equivalência
  MeasureRulerEquivalence eqVO = new MeasureRulerEquivalence();
  eqVO.getMeasureUnitHash().put(UnitUnit.UNIT, new BigDecimal("1")); // Definimos que cada 1 Unidade...
  eqVO.getMeasureUnitHash().put(WeightUnit.KILOGRAM, new BigDecimal("76")); // equivale a 76Kg...
  eqVO.getMeasureUnitHash().put(AreaUnit.SQUAREMETER, new BigDecimal("3.6")); // e a 3,6m²


Com as regras de equivalência o 'MeasureRuler conseguirá converter unidades conforme a necessidade. Vamos imaginar que o marceneiro precisa saber quantas chapas vamos precisar para cobrir uma área de 40m². Assim convertemos na régua:


Java 256.png Convertendo Entre as Dimensões
  // Chamamos a MeasureRuler, mas agora passando as regras de conversão
  BigDecimal v = MeasureRuler.convertTo(eqVO, new BigDecimal("40"), AreaUnit.SQUAREMETER, UnitUnit.UNIT, 6);

  // o Valor de v será equivalente a 11.111111, com 6 casas decimais de precisão, conforme solicitado


Unidades de Medidas Personalizadas

O MeasureRuler permite que o usuário crie suas próprias unidades de medidas bastando apenas definir um nome e um símbolo em uma classe que estenda a interface MeasureUnit. Depois, para que a unidade de medida se torne funcional basta adiciona-la a régua de equivalências como no exemplo anterior.

Imagine que o marceneiro do exemplo anterior, é de uma cidade esquisita e que lá existe uma unidade de medida "XYZ", utilizada localmente, cujo símbolo é "Xz" e que representa 7 Unidade. Para adicionar essa nova unidade na nossa equivalência bastaria criar um objeto implementando a interface MeasureUnit, como dito anteriormente. Veja o exemplo:


Java 256.png Unidade de Medida Personalizada
  // Mesma régua de equivalências do exemplo anterior
  MeasureRulerEquivalence eqVO = new MeasureRulerEquivalence();
  eqVO.getMeasureUnitHash().put(UnitUnit.UNIT, new BigDecimal("1"));
  eqVO.getMeasureUnitHash().put(WeightUnit.KILOGRAM, new BigDecimal("76"));
  eqVO.getMeasureUnitHash().put(AreaUnit.SQUAREMETER, new BigDecimal("3.6"));

  // Vamos utilizar a classe CustomMeasureUnitGeneric que implementa a interface necessária, para criar nossa unidade de medida personalizada
  CustomMeasureUnitGeneric xyz = new CustomMeasureUnitGeneric("XYZ", "Xz");

  // Agora incluímos nossa unidade de medida com o peso de 7 (pois é 7Xz para 1 Unidade)
  eqVO.getMeasureUnitHash().put(xyz, new BigDecimal("7"));


Note que o peso foi 7 porque nossa régua está criada com peso de 1 em unidades. Se nossa equivalência fosse 7Xz para 2 Unidades, teríamos que trocar o peso de 7 para 3,5Xz. Quando montamos a régua de equivalência temos que colocar os pesos que sejam compatíveis. No nosso exemplo, o código seria lido como se fosse a seguinte igualdade:

1 Unidade = 76Kg = 3,6m² = 7Xz


Equivalências Despareadas

Este é um recurso criado apenas para simplificar e permitir que o MeasureRuler faça os cálculos pra você no momento de adicionar as equivalências desconectadas.

Por exemplo, imagine que você tenha as seguinte igualdades de equivalência:

1 Unidade = 36g
2m² = 12,4Kg

Nessa situação teríamos que calcular as igualdade de maneira que se tornassem todas proporcionais, unificando a unidade de medida de Massa em "g" ou em "Kg" e recalculando a razão da outra unidade de medida. Até termos algo como:

1 Unidade = 36g => 0,001 Unidade = 0,036Kg => 0,3444444 Unidades = 12,4Kg
2m² = 12,4Kg

para que então possamos estabelecer a equivalência entre todos:

0,3444444 Unidades = 12,4Kg = 2m²


Para evitar esse trabalho todo externo a interface MeasureRulerEquivalenceInterface tem um método "default" chamado addComparativeEquivalence que permite adicionar essas referências descasadas diretamente. Vejamos o código no nosso exemplo acima:


Java 256.png Unidade de Medida Descasada
  // Criamos a régua e incluímos nossa primeira equivalência ligando duas dimensões
  MeasureRulerEquivalence eqVO = new MeasureRulerEquivalence();
  eqVO.getMeasureUnitHash().put(UnitUnit.UNIT, new BigDecimal("1"));
  eqVO.getMeasureUnitHash().put(WeightUnit.GRAM, new BigDecimal("36"));

  // Agora já temos Unidades e Massa na régua de equivalência, podemos adicionar outra dimensão utilizando uma dessas referência
  eqVO.addComparativeEquivalence(new BigDecimal("2"), AreaUnit.SQUAREMETER, new BigDecimal("12.4"), WeightUnit.KILOGRAM);


Internamente o método fará a conversão e estabelecerá os pesos corretos para conversão.



Note 64.png
Persistência de Unidades de Medidas Personalizadas
Vale ressaltar que as unidades de medidas personalizadas não podem ser persistidas com o MeasureUnitDAOConverter. Esses objetos devem ser persistidos em atributos separados como um objeto do sistema, ou o desenvolvedor deve implementar seu próprio converter para o determinado atributo.

Validação da MeasureRulerEquivalenceInterface

A classe MeasureRuller contém um método de validação para garantir que as informações da Hash de equivalências não estão redundantes ou incoerentes.