Eu sou um grande fã de verificação de tipo estático. Impede que você cometa erros estúpidos como este:
// java code
Adult a = new Adult();
a.setAge("Roger"); //static type checker would complain
a.setName(42); //and here too
Mas isso não impede que você cometa erros estúpidos como este:
Adult a = new Adult();
// obviously you've mixed up these fields, but type checker won't complain
a.setAge(150); // nobody's ever lived this old
a.setWeight(42); // a 42lb adult would have serious health issues
O problema ocorre quando você está usando o mesmo tipo para representar obviamente diferentes tipos de informações. Eu estava pensando que uma boa solução para isso seria estender a Integer
classe, apenas para evitar erros de lógica de negócios, mas não para adicionar funcionalidade. Por exemplo:
class Age extends Integer{};
class Pounds extends Integer{};
class Adult{
...
public void setAge(Age age){..}
public void setWeight(Pounds pounds){...}
}
Adult a = new Adult();
a.setAge(new Age(42));
a.setWeight(new Pounds(150));
Isso é considerado uma boa prática? Ou existem problemas imprevistos de engenharia no caminho com um design tão restritivo?
a.SetAge( new Age(150) )
Ainda não será compilado?new Age(...)
objeto, você não poderá atribuí-lo incorretamente a uma variável do tipoWeight
em nenhum outro local. Reduz o número de lugares onde erros podem acontecer.Respostas:
Você está essencialmente solicitando um sistema de unidades (não, não testes de unidade, "unidade" como em "unidade física", como medidores, volts etc.).
No seu código
Age
representa tempo ePounds
representa massa. Isso leva a coisas como conversão de unidades, unidades básicas, precisão etc.Houve / há tentativas de inserir algo assim em Java, por exemplo:
Os dois últimos parecem estar vivendo nessa coisa do github: https://github.com/unitsofmeasurement
C ++ possui unidades via Boost
O LabView vem com várias unidades .
Existem outros exemplos em outros idiomas. (edições são bem-vindas)
Como você pode ver acima, quanto mais um idioma for usado para manipular valores com unidades, mais nativamente ele suporta unidades. O LabView é frequentemente usado para interagir com dispositivos de medição. Como tal, faz sentido ter esse recurso no idioma e usá-lo certamente seria considerado uma boa prática.
Mas em qualquer linguagem de alto nível de uso geral, onde a demanda é baixa por uma quantidade tão rigorosa, provavelmente é inesperada.
Meu palpite seria: desempenho / memória. Se você lida com muitos valores, a sobrecarga de um objeto por valor pode se tornar um problema. Mas como sempre: a otimização prematura é a raiz de todo mal .
Eu acho que o maior "problema" é acostumar as pessoas, pois a unidade geralmente é definida implicitamente da seguinte forma:
As pessoas ficarão confusas quando tiverem que passar um objeto como um valor para algo que aparentemente poderia ser descrito de uma forma simples
int
, quando não estiverem familiarizados com os sistemas de unidades.fonte
int
..." --- para o qual temos aqui o diretor da menor surpresa. Boa pegada.Ao contrário da resposta de null, definir um tipo para uma "unidade" pode ser benéfico se um número inteiro não for suficiente para descrever a medida. Por exemplo, o peso é frequentemente medido em várias unidades dentro do mesmo sistema de medição. Pense em "libras" e "onças" ou "quilogramas" e "gramas".
Se você precisar de um nível de medição mais granular, definir um tipo para a unidade é benéfico:
Para algo como "idade", recomendo calcular em tempo de execução com base na data de nascimento da pessoa:
fonte
O que você parece estar procurando é conhecido como tipos com tags . Eles são uma maneira de dizer "este é um número inteiro que representa a idade", enquanto "este também é um número inteiro, mas representa o peso" e "você não pode atribuir um ao outro". Observe que isso vai além das unidades físicas, como metros ou quilogramas: no meu programa eu posso ter "alturas de pessoas" e "distâncias entre pontos de um mapa", ambos medidos em metros, mas não compatíveis entre si desde a atribuição de um. o outro não faz sentido da perspectiva da lógica de negócios.
Alguns idiomas, como o Scala, suportam tipos de tags com bastante facilidade (veja o link acima). Em outros, você pode criar suas próprias classes de wrapper, mas isso é menos conveniente.
Validação, por exemplo, verificar se a altura de uma pessoa é "razoável" é outra questão. Você pode colocar esse código em sua
Adult
classe (construtor ou setters) ou dentro de suas classes de tipo / wrapper marcadas. De certa forma, classes internas comoURL
ouUUID
cumprem essa função (entre outras, por exemplo, fornecendo métodos utilitários).Se o uso de tipos marcados ou classes de wrapper realmente ajudará a melhorar seu código, isso dependerá de vários fatores. Se seus objetos são simples e têm poucos campos, o risco de atribuí-los incorretamente é baixo e o código adicional necessário para usar tipos marcados pode não valer o esforço. Em sistemas complexos com estruturas complexas e muitos campos (especialmente se muitos deles compartilham o mesmo tipo primitivo), pode ser realmente útil.
No código que escrevo, geralmente crio classes de invólucro se passar por mapas. Tipos como eles
Map<String, String>
são muito opacos por si mesmos, então envolvê-los em classes com nomes significativos, comoNameToAddress
ajuda muito. Obviamente, com tipos marcados, você pode escreverMap<Name, Address>
e não precisar do wrapper para todo o mapa.No entanto, para tipos simples, como Strings ou Inteiros, eu achei as classes de wrapper (em Java) muito incômodas. A lógica comercial regular não era tão ruim, mas surgiram vários problemas com a serialização desses tipos para JSON, mapeando-os para objetos de banco de dados etc. Você pode escrever mapeadores e ganchos para todas as grandes estruturas (por exemplo, Jackson e Spring Data), mas o trabalho extra e a manutenção relacionados a esse código compensarão o que você ganha com o uso desses invólucros. Claro, YMMV e em outro sistema, o saldo pode ser diferente.
fonte