Eu me deparei com uma entrada de blog desencorajando o uso de Strings em Java para fazer com que seu código não tenha semântica, sugerindo que você deveria usar classes de wrapper finas. Estes são os exemplos anteriores e posteriores que a referida entrada fornece para ilustrar o assunto:
public void bookTicket(
String name,
String firstName,
String film,
int count,
String cinema);
public void bookTicket(
Name name,
FirstName firstName,
Film film,
Count count,
Cinema cinema);
Na minha experiência de ler blogs de programação, cheguei à conclusão de que 90% é um absurdo, mas fico me perguntando se esse é um ponto válido. De alguma forma, isso não parece certo para mim, mas eu não conseguia identificar exatamente o que está errado com o estilo de programação.
Respostas:
O encapsulamento existe para proteger seu programa contra alterações . A representação de um nome vai mudar? Caso contrário, você está desperdiçando seu tempo e YAGNI se aplica.
Edit: Eu li o post do blog e ele tem uma idéia fundamentalmente boa. O problema é que ele foi escalado demais. Algo como
String orderId
é muito ruim, porque presumivelmente"!"£$%^&*())ADAFVF
não é válidoorderId
. Isso significa queString
representa muito mais valores possíveis do que existemorderId
s válidos . No entanto, para algo como aname
, você não pode prever o que pode ou não ser um nome válido, e qualquerString
um é válidoname
.Na primeira instância, você está (corretamente) reduzindo as entradas possíveis para apenas as válidas. Na segunda instância, você não conseguiu restringir possíveis entradas válidas.
Editar novamente: considere o caso de entrada inválida. Se você escrever "Gareth Gobulcoque" como seu nome, parecerá bobo, não será o fim do mundo. Se você inserir um Código de Pedido inválido, é provável que ele simplesmente não funcione.
fonte
Name
, você não poderá passar acidentalmente outro valor String não relacionado. Onde você espera umName
, apenas umName
será compilado e a String "Eu devo $ 5" não pode ser acidentalmente aceita. (Note que este não está relacionado com qualquer tipo de validação de nome!)Isso é loucura :)
fonte
var p = new Point(x: 10, y:20);
Além disso, não é como o Cinema é tão diferente de uma string. Eu entenderia se alguém lidasse com quantidades físicas, como pressão, temperatura, energia, onde as unidades diferem e algumas não podem ser negativas. O autor do blog precisa tentar funcional.Eu concordo com o autor, principalmente. Se houver algum comportamento peculiar a um campo, como a validação de um ID de pedido, eu criaria uma classe para representar esse tipo. Seu segundo ponto é ainda mais importante: se você tiver um conjunto de campos que representam algum conceito como um endereço, crie uma classe para esse conceito. Se você está programando em Java, está pagando um preço alto pela digitação estática. Você também pode obter todo o valor possível.
fonte
Não faça isso; isso complicará demais as coisas e você não precisará disso
... é a resposta que eu teria escrito aqui há 2 anos. Agora, porém, não tenho tanta certeza; de fato, nos últimos meses, comecei a migrar códigos antigos para esse formato, não porque não tenho nada melhor para fazer, mas porque realmente precisava dele para implementar novos recursos ou alterar os existentes. Entendo as aversões automáticas que outras pessoas veem nesse código, mas acho que isso é algo que merece uma reflexão séria.
Benefícios
O principal entre os benefícios é a capacidade de modificar e estender o código. Se você usar
em vez de apenas passar alguns números inteiros - o que, infelizmente, muitas interfaces fazem -, fica muito mais fácil adicionar outra dimensão posteriormente. Ou mude o tipo para
double
. Se você usarList<Author> authors
ou emList<Person> authors
vezList<String> authors
disso mais tarde, será muito mais fácil adicionar mais informações ao que o autor representa. Anotá-lo dessa maneira, parece que estou dizendo o óbvio, mas, na prática, eu tenho sido culpado de usar strings dessa maneira muitas vezes, especialmente nos casos em que não era óbvio no começo, então eu precisaria de mais do que uma linha.No momento, estou tentando refatorar uma lista de cadeias de caracteres que está entrelaçada em todo o meu código, porque preciso de mais informações e sinto a dor: \
Além disso, concordo com o autor do blog que ele carrega mais informações semânticas , facilitando a compreensão do leitor. Embora os parâmetros geralmente recebam nomes significativos e obtenham uma linha de documentação dedicada, isso geralmente não é o caso de campos ou locais.
O benefício final é a segurança do tipo , por razões óbvias, mas aos meus olhos é uma coisa menor aqui.
Desvantagens
Leva mais tempo para escrever . Escrever uma turma pequena é rápido e fácil, mas não exige esforço, especialmente se você precisar de muitas dessas aulas. Se você parar a cada 3 minutos para escrever uma nova classe de wrapper, também poderá ser um prejuízo real para sua concentração. Eu gostaria de pensar, no entanto, que esse estado de esforço geralmente só acontece no primeiro estágio de escrever qualquer parte do código; Normalmente, eu posso ter uma boa idéia rapidamente de quais entidades precisarão estar envolvidas.
Pode envolver muitos setters redundantes (ou construções) e getters . O autor do blog dá o exemplo realmente feio de em
new Point(x(10), y(10))
vez denew Point(10, 10)
, e eu gostaria de acrescentar que um uso também pode envolver coisas como emMath.max(p.x.get(), p.y.get())
vez deMath.max(p.x, p.y)
. E código longo é geralmente considerado mais difícil de ler, e com justiça. Mas, com toda a honestidade, tenho a sensação de que muitos códigos movem objetos, e apenas métodos selecionados o criam, e menos ainda precisam acessar seus detalhes difíceis (o que não é OOPy).Discutível
Eu diria se isso ajuda ou não a legibilidade do código é discutível. Sim, mais informações semânticas, mas código mais longo. Sim, é mais fácil entender o papel de cada local, mas é mais difícil entender o que você pode fazer com ele, a menos que leia a documentação.
Como na maioria das outras escolas de programação, acho que não é saudável levar essa ao extremo. Eu nunca me vejo separando as coordenadas xey para ser de um tipo diferente. Eu não acho que
Count
é necessário quando umint
deve ser suficiente. Não gosto dounsigned int
uso em C - embora teoricamente bom, ele não fornece informações suficientes e proíbe estender seu código posteriormente para suportar esse -1 mágico. Às vezes você precisa de simplicidade.Eu acho que o post do blog é um pouco extremo. Mas, no geral, aprendi por experiência dolorosa que a idéia básica por trás disso é feita com as coisas certas.
Tenho uma aversão profunda ao código de engenharia excessiva. Eu realmente faço. Mas, usado corretamente, não acho que isso exagere na engenharia.
fonte
Embora seja um exagero, muitas vezes acho que a maioria das coisas que vi são pouco projetadas.
Não é apenas "Segurança". Uma das coisas realmente boas sobre Java é que ele ajuda muito a lembrar / descobrir exatamente o que qualquer método de biblioteca chamado / precisa / espera.
A PIOR (de longe) biblioteca Java com a qual trabalhei foi escrita por alguém que gostava muito de Smalltalk e modelou a biblioteca GUI para fazer com que ela parecesse mais com smalltalk - o problema é que todo método tem o mesmo objeto base, mas na verdade não podia USAR tudo o que o objeto base poderia ser convertido, então você voltou a adivinhar o que passar nos métodos e sem saber se havia falhado até o tempo de execução (algo que eu costumava lidar todas as vezes que eu estava trabalhando em C).
Outra questão - se você passar strings, ints, coleções e matrizes sem objetos, tudo o que você tem são bolas de dados sem significado. Isso parece natural quando você pensa em termos de bibliotecas que "algum aplicativo" usará, mas ao projetar um aplicativo inteiro, é muito mais útil atribuir significado (código) a todos os seus dados no local em que os dados são definidos e pensar apenas em termos de como esses objetos de alto nível interagem. Se você está passando primitivas em vez de objetos, está alterando, por definição, os dados em um local diferente do local onde está definido (também é por isso que eu realmente não gosto de Setters e Getters - mesmo conceito, você está operando em dados que não são seus).
Por fim, se você definir objetos separados para tudo, sempre terá um ótimo lugar para validar tudo - por exemplo, se você criar um objeto para código postal e, posteriormente, descobrir que precisa garantir que o CEP sempre inclua a extensão de 4 dígitos, você tem um lugar perfeito para colocá-lo.
Na verdade, não é uma má ideia. Pensando nisso, eu nem tenho certeza se diria que foi superprojetado, é mais fácil trabalhar com quase todos os aspectos - a única exceção é a proliferação de pequenas classes, mas as classes Java são muito leves e fáceis escrever que isso dificilmente é um custo (eles podem até ser gerados).
Eu estaria realmente interessado em ver um projeto java bem escrito que realmente tinha muitas classes definidas (onde isso dificultava a programação), estou começando a pensar que não é possível ter muitas classes.
fonte
Eu acho que você tem que olhar para esse conceito de outro ponto de partida. Dê uma olhada na perspectiva de um designer de banco de dados: os tipos passados no primeiro exemplo não definem seus parâmetros de uma maneira única, muito menos de uma maneira útil.
São necessários dois parâmetros para especificar o consumidor real que está registrando os ingressos. Você pode ter dois filmes diferentes com nomes idênticos (por exemplo, remakes), ou o mesmo filme com nomes diferentes (por exemplo, traduções). Uma certa cadeia de cinemas pode ter filiais diferentes, então como você vai lidar com isso de uma maneira consistente e consistente (por exemplo, você está usando
$chain ($city)
ou$chain in $city
ou mesmo outra coisa e como garantir que isso seja Na verdade, o pior é especificar seu usuário por meio de dois parâmetros, o fato de que o nome e o sobrenome sejam fornecidos não garante um cliente válido (e você não pode distinguir doisJohn Doe
).A resposta para isso é declarar tipos, mas esses raramente serão invólucros finos, como mostramos acima. Provavelmente, eles funcionarão como seu armazenamento de dados ou serão acoplados a algum tipo de banco de dados. Portanto, um
Cinema
objeto provavelmente terá um nome, local ... e assim você se livra dessas ambiguidades. Se são embalagens finas, são por coincidência.Então, IMHO, o post do blog está apenas dizendo "certifique-se de passar os tipos corretos", seu autor acabou de fazer a escolha excessivamente restrita de escolher tipos de dados básicos em particular (que é a mensagem errada).
A alternativa proposta é melhor:
Por outro lado, acho que o post do blog vai longe demais em tudo.
Count
é muito genérico, eu poderia contar maçãs ou laranjas com isso, adicioná-las e ainda ter uma situação em minhas mãos em que o sistema de tipos me permita realizar operações sem sentido. É claro que você pode aplicar a mesma lógica do blog e definir tiposCountOfOranges
etc., mas isso também é bobagem.Pelo que vale a pena, eu escreveria algo como
Para encurtar a história: você não deve passar variáveis sem sentido; as únicas vezes em que você realmente especificou um objeto com um valor que não determina o objeto real é quando você executa uma consulta (por exemplo
public Collection<Film> findFilmsWithTitle(String title)
) ou quando está montando uma prova de conceito. Mantenha seu sistema de tipos limpo, portanto, não use um tipo que seja muito geral (por exemplo, um filme representado por aString
) ou muito restritivo / específico / artificial (por exemplo, emCount
vez deint
). Use um tipo que defina seu objeto de maneira única e inequívoca sempre que possível e viável.editar : um resumo ainda mais curto. Para pequenas aplicações (por exemplo, prova de conceitos): por que se preocupar com um design complicado? Basta usar
String
ouint
e continuar com isso.Para aplicativos grandes: é realmente provável que você tenha muitas classes que consistem em um único campo com um tipo de dados básico? Se você tem poucas classes desse tipo, apenas objetos "normais", nada de especial acontecendo lá.
Sinto que a idéia de encapsular seqüências de caracteres ... é apenas um projeto incompleto: excessivamente complicado para aplicativos pequenos, não completo o suficiente para aplicativos grandes.
fonte
Para mim, fazer isso é o mesmo que usar regiões em C #. Geralmente, se você acha que isso é necessário para tornar seu código legível, você tem problemas maiores nos quais deve gastar seu tempo.
fonte
Eu diria que é realmente uma boa idéia em um idioma com um recurso tipo typedef fortemente tipado.
Em Java, você não tem isso; portanto, criar uma classe totalmente nova para essas coisas significa que o custo provavelmente supera o benefício. Você também pode obter 80% do benefício tomando cuidado com a nomeação de variáveis / parâmetros.
fonte
Seria bom se IF String (end Integer e ... falando apenas sobre String) não fosse final, portanto essas classes poderiam ser uma String (restrita) com significado e ainda poderem ser enviadas para algum objeto independente que sabe lidar com isso. o tipo de base (sem conversar para frente e para trás).
E "goodnes" aumentam quando existem, por exemplo. restrições em todos os nomes.
Mas, ao criar algum aplicativo (não biblioteca), é sempre possível refatorá-lo. Então prefiro começar sem ele.
fonte
Para dar um exemplo: em nosso sistema desenvolvido atualmente, existem muitas entidades diferentes que podem ser identificadas por vários tipos de identificadores (devido ao uso de sistemas externos), às vezes até o mesmo tipo de entidade. Todos os identificadores são Strings - portanto, se alguém misturar qual tipo de identificação deve ser passado como parâmetro, nenhum erro de tempo de compilação será mostrado, mas o programa explodirá em tempo de execução. Isso acontece com bastante frequência. Portanto, devo dizer que o objetivo principal desse princípio não é proteger contra mudanças (embora isso também sirva), mas proteger-nos de bugs. De qualquer forma, se alguém cria uma API, ela deve refletir os conceitos do domínio, por isso é conceitualmente útil definir classes específicas do domínio - tudo depende da existência de ordem na mente dos desenvolvedores,
fonte