O encapsulamento diz-me para tornar todos ou quase todos os campos privados e expô-los por getters / setters. Mas agora aparecem bibliotecas como o Lombok, que nos permitem expor todos os campos particulares com uma breve anotação @Data
. Ele criará getters, setters e construtores de configuração para todos os campos particulares.
Alguém poderia me explicar qual é o sentido de ocultar todos os campos como privados e depois expor todos eles por alguma tecnologia extra? Por que simplesmente não usamos apenas campos públicos? Sinto que percorremos um caminho longo e difícil apenas para retornar ao ponto de partida.
Sim, existem outras tecnologias que funcionam através de getters e setters. E não podemos usá-los através de campos públicos simples. Mas essas tecnologias apareceram apenas porque tínhamos essas inúmeras propriedades - campos privados por trás de getters / setters públicos. Se não tivéssemos as propriedades, essas tecnologias se desenvolveriam de outra maneira e suportariam campos públicos. E tudo seria simples e não precisaríamos mais do Lombok agora.
Qual é o sentido de todo esse ciclo? E o encapsulamento tem realmente algum sentido agora na programação da vida real?
fonte
"It will create getters, setters and setting constructors for all private fields."
- Do jeito que você descreve essa ferramenta, parece que ela está mantendo o encapsulamento. (Pelo menos em um sentido frouxo, automatizado e de certa forma anêmico). Então, qual é exatamente o problema?Respostas:
Se você expõe todos os seus atributos com getters / setters, está obtendo apenas uma estrutura de dados que ainda é usada em C ou em qualquer outra linguagem processual. É não encapsulamento e Lombok é apenas fazer para trabalhar com código de procedimento menos doloroso. Getters / setters tão ruins quanto campos públicos simples. Realmente não há diferença.
E a estrutura de dados não é um objeto. Se você começar a criar um objeto escrevendo uma interface, nunca adicionará getters / setters à interface. A exposição de seus atributos leva ao código processual de espaguete, onde a manipulação com dados está fora de um objeto e se espalha por toda a base de código. Agora você está lidando com dados e com manipulações com dados em vez de falar com objetos. Com getters / setters, você terá uma programação procedural orientada a dados, onde a manipulação é feita de maneira absolutamente imperativa. Obtenha dados - faça alguma coisa - defina dados.
No POO, o encapsulamento é um elefante, se feito da maneira correta. Você deve encapsular os detalhes do estado e da implementação para que o objeto tenha controle total sobre isso. A lógica será focada no objeto e não será espalhada por toda a base de código. E sim - o encapsulamento ainda é essencial na programação, pois o código será mais sustentável.
EDITAR% S
Depois de ver as discussões, quero adicionar várias coisas:
Portanto, se voltarmos à pergunta por que deveríamos fazer isso. Considere este exemplo simples:
Aqui temos o documento expondo detalhes internos pelo getter e temos um código de procedimento externo em
printDocument
função que funciona com o que está fora do objeto. Por que isso é ruim? Porque agora você só tem código no estilo C. Sim, está estruturado, mas qual é realmente a diferença? Você pode estruturar funções C em arquivos diferentes e com nomes. E as chamadas camadas estão fazendo exatamente isso. A classe de serviço é apenas um monte de procedimentos que funcionam com dados. Esse código é menos sustentável e tem muitas desvantagens.Compare com este. Agora temos um contrato e os detalhes de implementação deste contrato estão ocultos dentro do objeto. Agora você pode realmente testar essa classe e essa classe está encapsulando alguns dados. O modo como ele trabalha com esses dados é um objeto preocupante. Para falar com o objeto agora, você precisa solicitar que ele se imprima. Isso é encapsulamento e isso é um objeto. Você obterá todo o poder da injeção de dependência, zombaria, teste, responsabilidades únicas e muitos benefícios com o OOP.
fonte
A sensação é de que você não deveria fazer isso .
Encapsulamento significa que você expõe apenas os campos que realmente precisa de outras classes para acessar e que é muito seletivo e cuidadoso.
Você não apenas dar todos os campos getters e setters por padrão!
Isso é totalmente contra o espírito da especificação JavaBeans, que é ironicamente a origem do conceito de getters e setters públicos.
Mas se você olhar para as especificações, verá que a criação de getters e setters é muito seletiva e fala sobre propriedades "somente leitura" (sem setter) e propriedades "somente leitura" (sem getter).
Outro fator é que getters e setters não são necessariamente simples acesso ao campo privado . Um getter pode calcular o valor que retorna de uma maneira arbitrariamente complexa, talvez armazená-lo em cache. Um setter pode validar o valor ou notificar os ouvintes.
Então aí está: encapsulamento significa que você expõe apenas a funcionalidade que realmente precisa expor. Mas se você não pensa no que precisa expor e apenas quer expor tudo seguindo alguma transformação sintática, é claro que isso não é realmente um encapsulamento.
fonte
getFieldName()
se tornam um contrato automático para nós. Não esperaremos algum comportamento complexo por trás disso. Na maioria dos casos, é apenas uma quebra direta do encapsulamento.Eu acho que o cerne da questão é explicado pelo seu comentário:
O problema que você tem é que você está misturando o modelo de dados de persistência com o modelo de dados ativo .
Um aplicativo geralmente terá vários modelos de dados:
além do modelo de dados que ele realmente usa para realizar seus cálculos.
Em geral, os modelos de dados usados para comunicação com o exterior devem ser isolados e independentes do modelo de dados interno (modelo de objeto de negócios, BOM) no qual os cálculos são executados:
Nesse cenário, é perfeitamente aceitável que os objetos usados nas camadas de comunicação tenham todos os itens públicos ou expostos por getters / setters. Esses são objetos simples, sem invariáveis.
Por outro lado, sua lista técnica deve ter invariantes, o que geralmente impede muitos montadores (getters não afetam os invariantes, embora reduzam o encapsulamento em um grau).
fonte
Considere o seguinte..
Você tem uma
User
classe com uma propriedadeint age
:Você quer ampliar isso para
User
que a data de nascimento seja oposta a apenas uma idade. Usando um getter:Podemos trocar o
int age
campo por um mais complexoLocalDate dateOfBirth
:Sem violações de contrato, sem quebra de código. Nada mais do que escalar a representação interna em preparação para comportamentos mais complexos.
O próprio campo é encapsulado.
Agora, para esclarecer as preocupações ..
A
@Data
anotação de Lombok é semelhante às classes de dados de Kotlin .Nem todas as classes representam objetos comportamentais. Quanto à quebra do encapsulamento, depende do uso do recurso. Você não deve expor todos os seus campos por meio de getters.
Em um sentido mais geral, encapsulamento é o ato de ocultar informações. Se você abusar
@Data
, é fácil supor que você provavelmente está quebrando o encapsulamento. Mas isso não quer dizer que não tenha propósito. O JavaBeans, por exemplo, é desaprovado por alguns. No entanto, é amplamente utilizado no desenvolvimento empresarial.Você concluiria que o desenvolvimento da empresa é ruim devido ao uso de beans? Claro que não! Os requisitos diferem dos do desenvolvimento padrão. O feijão pode ser abusado? Claro! Eles são abusados o tempo todo!
O Lombok também suporta
@Getter
e@Setter
independentemente - use o que seus requisitos exigem.fonte
@Data
anotação em um tipo, que por design desencapsula todos os campossetAge(xyz)
Não é assim que o encapsulamento é definido na programação orientada a objetos. Encapsulamento significa que cada objeto deve ser como uma cápsula, cuja concha externa (a API pública) protege e regula o acesso ao seu interior (os métodos e campos privados) e a oculta da vista. Ao ocultar internos, os chamadores não dependem de internos, permitindo que os internos sejam alterados sem alterar (ou mesmo recompilar) os chamadores. Além disso, o encapsulamento permite que cada objeto imponha seus próprios invariantes, disponibilizando apenas operações seguras para os chamadores.
O encapsulamento é, portanto, um caso especial de ocultação de informações, no qual cada objeto oculta suas partes internas e aplica seus invariantes.
A geração de getters e setters para todos os campos é uma forma bastante fraca de encapsulamento, porque a estrutura dos dados internos não está oculta e os invariantes não podem ser impostos. Ele tem a vantagem de poder alterar a maneira como os dados são armazenados internamente (desde que você possa converter de e para a estrutura antiga) sem precisar alterar (ou mesmo recompilar) os chamadores.
Parcialmente, isso ocorre devido a um acidente histórico. A primeira é que, em Java, as expressões de chamada de método e de acesso ao campo são sintaticamente diferentes no site da chamada, ou seja, a substituição de um acesso ao campo por uma chamada getter ou setter quebra a API de uma classe. Portanto, se você precisar de um acessador, deverá escrever um agora ou poder quebrar a API. Essa ausência de suporte a propriedades no nível da linguagem contrasta fortemente com outras linguagens modernas, principalmente C # e EcmaScript .
A greve 2 é que a especificação JavaBeans definiu propriedades como getters / setters, os campos não eram propriedades. Como resultado, a maioria das estruturas corporativas anteriores suportava getters / setters, mas não campos. Isso já faz muito tempo ( API de persistência Java (JPA) , validação de bean , arquitetura Java para ligação a XML (JAXB) , Jacksontodos os campos de suporte estão bem agora), mas os tutoriais e livros antigos continuam demorando, e nem todo mundo sabe que as coisas mudaram. A ausência de suporte a propriedades no nível do idioma ainda pode ser um problema em alguns casos (por exemplo, porque o carregamento lento de JPA de entidades únicas não é acionado quando os campos públicos são lidos), mas a maioria dos campos públicos funciona muito bem. Ou seja, minha empresa grava todos os DTOs para suas APIs REST com campos públicos (afinal, não é mais público transmitido pela Internet :-).
Dito isto, Lombok de
@Data
faz mais do que gerar getters / setters: Gera tambémtoString()
,hashCode()
eequals(Object)
, o que pode ser bastante valiosa.O encapsulamento pode ser inestimável ou totalmente inútil, depende do objeto que está sendo encapsulado. Geralmente, quanto mais complexa a lógica dentro da classe, maior o benefício do encapsulamento.
Os getters e setters gerados automaticamente para cada campo geralmente são usados em excesso, mas podem ser úteis para trabalhar com estruturas herdadas ou para usar o recurso ocasional da estrutura não suportado para campos.
O encapsulamento pode ser alcançado com getters e métodos de comando. Os setters geralmente não são apropriados, porque é esperado que eles alterem apenas um único campo, enquanto a manutenção de invariantes pode exigir que vários campos sejam alterados ao mesmo tempo.
Sumário
getters / setters oferecem um encapsulamento bastante ruim.
A prevalência de getters / setters em Java decorre da falta de suporte no nível da linguagem para propriedades e opções de design questionáveis em seu modelo de componente histórico que foram consagrados em muitos materiais de ensino e nos programadores ensinados por eles.
Outras linguagens orientadas a objetos, como o EcmaScript, suportam propriedades no nível da linguagem, para que os getters possam ser introduzidos sem interromper a API. Nessas linguagens, os getters podem ser introduzidos quando você realmente precisa deles, e não antes do tempo, apenas no caso de precisar de um dia, o que contribui para uma experiência de programação muito mais agradável.
fonte
Eu realmente me fiz essa pergunta.
Não é totalmente verdade embora. A prevalência de IMO de getters / setters é causada pela especificação do Java Bean, que exige isso; portanto, é praticamente um recurso não da programação orientada a objetos, mas da programação orientada a Bean, se você preferir. A diferença entre os dois é a da camada de abstração em que eles existem; O feijão é mais uma interface do sistema, ou seja, em uma camada superior. Eles abstraem das bases OO, ou pretendem, pelo menos - como sempre, as coisas estão sendo levadas com muita frequência.
Eu diria que é um pouco lamentável que essa coisa do Bean, onipresente na programação Java, não seja acompanhada pela adição de um recurso correspondente da linguagem Java - estou pensando em algo como o conceito de Propriedades em C #. Para quem não está ciente disso, é uma construção de linguagem que se parece com isso:
De qualquer forma, o âmago da questão da implementação real ainda se beneficia muito do encapsulamento.
fonte
A resposta simples aqui é: você está absolutamente certo. Os getters e setters eliminam a maioria (mas não todos) do valor do encapsulamento. Isso não quer dizer que sempre que você tiver um método get e / ou set que esteja quebrando o encapsulamento, mas se estiver adicionando cegamente acessadores a todos os membros particulares da sua classe, estará fazendo errado.
Getters são setters são onipresentes na programação Java porque o conceito JavaBean foi empurrado como uma maneira de vincular dinamicamente a funcionalidade ao código pré-criado. Por exemplo, você pode ter um formulário em um applet (alguém se lembra deles?) Que inspecionaria seu objeto, encontraria todas as propriedades e exibisse os campos como. Em seguida, a interface do usuário pode modificar essas propriedades com base na entrada do usuário. Você, como desenvolvedor, apenas se preocupa em escrever a classe e coloca qualquer validação ou lógica de negócios lá etc.
Usando Beans de Exemplo
Essa não é uma idéia terrível, por si só, mas nunca fui um grande fã da abordagem em Java. Está apenas indo contra a corrente. Use Python, Groovy etc. Algo que suporte esse tipo de abordagem de maneira mais natural.
A coisa do JavaBean meio que ficou fora de controle porque criou o JOBOL, ou seja, desenvolvedores escritos em Java que não entendem OO. Basicamente, os objetos se tornaram nada mais do que pacotes de dados e toda a lógica foi escrita fora em métodos longos. Por ser visto como normal, pessoas como você e eu, que questionamos isso, eram consideradas malucas. Ultimamente eu tenho visto uma mudança e isso não é tanto uma posição de fora
A coisa da ligação XML é muito difícil. Provavelmente, este não é um bom campo de batalha para se posicionar contra o JavaBeans. Se você precisar criar esses JavaBeans, tente mantê-los fora do código real. Trate-os como parte da camada de serialização.
fonte
[Serializable]
atributo e de seus parentes. Por que escrever 101 métodos / classes específicos de serialização quando você pode apenas escrever um, aproveitando a reflexão?Quanto podemos fazer sem getters? É possível removê-los completamente? Que problemas isso cria? Poderíamos até banir a
return
palavra - chave?Acontece que você pode fazer muito se estiver disposto a fazer o trabalho. Como, então, as informações saem desse objeto totalmente encapsulado? Através de um colaborador.
Em vez de permitir que o código faça perguntas, você deve dizer o que deve fazer. Se essas coisas também não retornam coisas, você não precisa fazer nada sobre o que elas retornam. Portanto, quando estiver pensando
return
em buscar um, tente procurar algum colaborador da porta de saída que fará o que resta a ser feito.Fazer as coisas dessa maneira tem benefícios e consequências. Você tem que pensar mais do que apenas o que você teria retornado. Você precisa pensar em como enviar isso como uma mensagem para um objeto que não pediu. Pode ser que você desmaie o mesmo objeto que retornaria, ou simplesmente chamar um método é suficiente. Fazer esse pensamento tem um custo.
O benefício é que agora você está falando de frente para a interface. Isso significa que você obtém todos os benefícios da abstração.
Também fornece despacho polimórfico, porque enquanto você sabe o que está dizendo, não precisa saber exatamente para o que está dizendo.
Você pode pensar que isso significa que você precisa percorrer apenas um caminho através de uma pilha de camadas, mas acontece que você pode usar o polimorfismo para retroceder sem criar dependências cíclicas loucas.
Pode ser assim:
Se você pode codificar assim, usar getters é uma opção. Não é um mal necessário.
fonte
Esta é uma questão controversa (como você pode ver), porque um monte de dogmas e mal-entendidos se confunde com as preocupações razoáveis em relação à questão dos caçadores e levantadores. Mas, em resumo, não há nada de errado com
@Data
isso e não quebra o encapsulamento.Por que usar getters e setters em vez de campos públicos?
Porque getters / setters fornecem encapsulamento. Se você expõe um valor como um campo público e depois muda para o cálculo em tempo real, precisará modificar todos os clientes que acessam o campo. Claramente isso é ruim. É um detalhe de implementação se alguma propriedade de um objeto é armazenada em um campo, é gerada em tempo real ou é buscada em outro lugar, portanto, a diferença não deve ser exposta aos clientes. Getter / setter setter resolvem isso, pois ocultam a implementação.
Mas se o getter / setter reflete apenas um campo privado subjacente, não é tão ruim assim?
Não! O ponto é que o encapsulamento permite alterar a implementação sem afetar os clientes. Um campo ainda pode ser uma maneira perfeitamente adequada de armazenar valor, desde que os clientes não precisem saber ou se importar.
Mas os geradores / setters de geração automática de campos não estão quebrando o encapsulamento?
Não, o encapsulamento ainda está lá!
@Data
as anotações são apenas uma maneira conveniente de escrever pares getter / setter que usam um campo subjacente. Para o ponto de vista de um cliente, isso é exatamente como um par getter / setter comum. Se você decidir reescrever a implementação, ainda poderá fazê-lo sem afetar o cliente. Assim, você obtém o melhor dos dois mundos: encapsulamento e sintaxe concisa.Mas alguns dizem que os getter / setters são sempre ruins!
Há uma controvérsia separada, na qual alguns acreditam que o padrão getter / setter é sempre ruim, independentemente da implementação subjacente. A idéia é que você não defina ou obtenha valores de objetos, mas qualquer interação entre objetos deve ser modelada como mensagens em que um objeto pede que outro objeto faça alguma coisa. Isso é principalmente um dogma dos primeiros dias do pensamento orientado a objetos. O pensamento agora é que, para certos padrões (por exemplo, objetos de valor, objeto de transferência de dados), os getters / setters podem ser perfeitamente apropriados.
fonte
O encapsulamento tem um propósito, mas também pode ser mal utilizado ou abusado.
Considere algo como a API do Android, que possui classes com dezenas (se não centenas) de campos. Ao expor esses campos, o consumidor da API dificulta a navegação e o uso, além de fornecer ao usuário a falsa noção de que ele pode fazer o que quiser com aqueles campos que podem entrar em conflito com a forma como devem ser usados. Portanto, o encapsulamento é ótimo nesse sentido para manutenção, usabilidade, legibilidade e evitar bugs malucos.
Por outro lado, POD ou tipos de dados antigos simples, como uma estrutura do C / C ++, na qual todos os campos são públicos, também podem ser úteis. Ter getters / setters inúteis como os gerados pela anotação @data no Lombok é apenas uma maneira de manter o "Padrão de encapsulamento". Uma das poucas razões pelas quais fazemos getters / inúteis "inúteis" em Java é que os métodos fornecem um contrato .
Em Java, você não pode ter campos em uma interface; portanto, você usa getters e setters para especificar uma propriedade comum que todos os implementadores dessa interface possuem. Em idiomas mais recentes, como Kotlin ou C #, vemos o conceito de propriedades como campos para os quais você pode declarar um setter e um getter. No final, getters / setters inúteis são mais um legado que o Java tem que conviver, a menos que a Oracle adicione propriedades a ele. O Kotlin, por exemplo, que é outra linguagem da JVM desenvolvida pela JetBrains, possui classes de dados que basicamente fazem o que a anotação @data faz no Lombok.
Também aqui estão alguns exemplos:
Este é um caso ruim de encapsulamento. O getter e o setter são efetivamente inúteis. O encapsulamento é usado principalmente porque esse é o padrão em linguagens como Java. Na verdade, não ajuda, além de manter a consistência na base de código.
Este é um bom exemplo de encapsulamento. Encapsulamento é usado para impor um contrato, neste caso IDataInterface. O objetivo do encapsulamento neste exemplo é fazer com que o consumidor dessa classe use os métodos fornecidos pela interface. Embora o getter e o setter não façam nada sofisticado, agora definimos uma característica comum entre o DataClass e outros implementadores do IDataInterface. Assim, eu posso ter um método como este:
Agora, ao falar sobre encapsulamento, acho importante abordar também o problema de sintaxe. Muitas vezes vejo pessoas reclamando da sintaxe necessária para impor o encapsulamento em vez do próprio encapsulamento. Um exemplo que vem à mente é de Casey Muratori (você pode ver o discurso dele aqui ).
Suponha que você tenha uma classe de jogador que usa encapsulamento e deseja mover sua posição em 1 unidade. O código ficaria assim:
Sem encapsulamento, ficaria assim:
Aqui ele argumenta que os encapsulamentos levam a muito mais digitação sem benefícios adicionais e isso pode, em muitos casos, ser verdade, mas observe alguma coisa. O argumento é contra a sintaxe, não o próprio encapsulamento. Mesmo em linguagens como C que não possuem o conceito de encapsulamento, muitas vezes você vê variáveis em estruturas pré-expressas ou sufixadas com '_' ou 'my' ou qualquer outra coisa que signifique que elas não devem ser usadas pelo consumidor da API, como se fossem privado.
O fato é que o encapsulamento pode ajudar a tornar o código muito mais sustentável e fácil de usar. Considere esta classe:
Se as variáveis fossem públicas neste exemplo, um consumidor dessa API ficaria confuso sobre quando usar posX e posY e quando usar setPosition (). Ao ocultar esses detalhes, você ajuda o consumidor a usar melhor sua API de maneira intuitiva.
A sintaxe é uma limitação em muitos idiomas. No entanto, os idiomas mais novos oferecem propriedades que nos dão a boa sintaxe dos membros da publicação e os benefícios do encapsulamento. Você encontrará propriedades em C #, Kotlin, mesmo em C ++, se você usar o MSVC. Aqui está um exemplo no Kotlin.
classe VerticalList: ... {var posX: Int set (x) {field = x; ...} var posY: Int set (y) {campo = y; ...}}
Aqui conseguimos o mesmo que no exemplo Java, mas podemos usar posX e posY como se fossem variáveis públicas. Porém, quando tento alterar o valor deles, o corpo do setter set () será executado.
No Kotlin, por exemplo, isso seria o equivalente a um Java Bean com getters, setters, hashcode, equals e toString implementados:
Observe como essa sintaxe nos permite fazer um Java Bean em uma linha. Você percebeu corretamente o problema de uma linguagem como Java na implementação do encapsulamento, mas isso é culpa do Java, não do próprio encapsulamento.
Você disse que usa o @Data do Lombok para gerar getters e setters. Observe o nome, @Data. Ele deve ser usado principalmente em classes de dados que armazenam apenas dados e devem ser serializadas e desserializadas. Pense em algo como um arquivo salvo de um jogo. Mas em outros cenários, como em um elemento de interface do usuário, você definitivamente deseja setters, pois apenas alterar o valor de uma variável pode não ser suficiente para obter o comportamento esperado.
fonte
O encapsulamento oferece flexibilidade . Ao separar estrutura e interface, permite alterar a estrutura sem alterar a interface.
Por exemplo, se você achar que precisa calcular uma propriedade com base em outros campos, em vez de inicializar o campo subjacente na construção, basta alterar o getter. Se você tivesse exposto o campo diretamente, teria que alterar a interface e fazer alterações em todos os sites de uso.
fonte
Tentarei ilustrar o espaço problemático do encapsulamento e do design da classe e responderei sua pergunta no final.
Como mencionado em outras respostas, o objetivo do encapsulamento é ocultar detalhes internos de um objeto atrás de uma API pública, que serve como contrato. É seguro alterar o objeto interno porque sabe que é chamado apenas através da API pública.
Se faz sentido ter campos públicos, getters / setters ou métodos de transação de nível superior ou passagem de mensagens, depende da natureza do domínio que está sendo modelado. No livro Akka Concurrency (que eu posso recomendar, mesmo que esteja um pouco desatualizado), você encontra um exemplo que ilustra isso, que abreviarei aqui.
Considere uma classe de usuário:
Isso funciona bem em um contexto de thread único. O domínio que está sendo modelado é o nome de uma pessoa e a mecânica de como esse nome é armazenado pode ser perfeitamente encapsulada pelos setters.
No entanto, imagine que isso deve ser fornecido em um contexto multithread. Suponha que um segmento esteja lendo periodicamente o nome:
E dois outros tópicos estão lutando contra um cabo de guerra, definindo-o para Hillary Clinton e Donald Trump, por sua vez. Cada um deles precisa chamar dois métodos. Principalmente isso funciona bem, mas de vez em quando você vê uma Hillary Trump ou um Donald Clinton passando.
Você não pode resolver esse problema adicionando uma trava dentro dos setters, porque a trava é mantida apenas durante o período de configuração do nome ou sobrenome. A única solução através do bloqueio é adicionar um bloqueio ao redor do objeto inteiro, mas isso interrompe o encapsulamento, pois o código de chamada deve gerenciar o bloqueio (e pode causar conflitos).
Como se vê, não há solução limpa através do bloqueio. A solução limpa é encapsular os internos novamente, tornando-os mais grosseiros:
O nome em si tornou-se imutável e você vê que seus membros podem ser públicos, porque agora é um objeto de dados puro, sem capacidade de modificá-lo depois de criado. Por sua vez, a API pública da classe User tornou-se mais grossa, com apenas um único setter restante, para que o nome só possa ser alterado como um todo. Ele encapsula mais de seu estado interno por trás da API.
O que você vê neste ciclo são tentativas de aplicar soluções boas para um conjunto específico de circunstâncias de maneira muito ampla. Um nível adequado de encapsulamento requer a compreensão do domínio que está sendo modelado e a aplicação do nível correto de encapsulamento. Às vezes, isso significa que todos os campos são públicos, outras (como nos aplicativos Akka) significa que você não possui uma API pública, exceto por um único método para receber mensagens. No entanto, o próprio conceito de encapsulamento, que significa ocultar os internos por trás de uma API estável, é essencial para programar software em escala, especialmente em sistemas multithread.
fonte
Posso pensar em um caso de uso em que isso faz sentido. Você pode ter uma classe que acessa originalmente por meio de uma simples API getter / setter. Posteriormente, você estende ou modifica para que não use mais os mesmos campos, mas ainda suporte a mesma API .
Um exemplo um tanto artificial: um ponto que começa como um par cartesiano com
p.x()
ep.y()
. Mais tarde, você cria uma nova implementação ou subclasse que usa coordenadas polares, para que você também possa chamarp.r()
ep.theta()
, mas seu código de cliente que chamap.x()
ep.y()
permanece válido. A própria classe converte de forma transparente a partir da forma polar interna, isto é,y()
faria agorareturn r * sin(theta);
. (Neste exemplo, definir apenasx()
ouy()
não faz tanto sentido, mas ainda é possível.)Nesse caso, você pode estar dizendo: “Ainda bem que me preocupei em declarar automaticamente getters e setters em vez de tornar os campos públicos, ou teria que interromper minha API lá”.
fonte
Não há absolutamente nenhum sentido. No entanto, o fato de você fazer essa pergunta demonstra que você não entendeu o que Lombok faz e que não entende como escrever código OO com encapsulamento. Vamos voltar um pouco ...
Alguns dados para uma instância de classe sempre serão internos e nunca devem ser expostos. Alguns dados para uma instância de classe precisarão ser configurados externamente e alguns dados poderão precisar ser transferidos de volta para uma instância de classe. No entanto, podemos mudar a forma como a classe faz as coisas sob a superfície, então usamos funções para nos permitir obter e definir dados.
Alguns programas desejam salvar o estado para instâncias de classe, portanto, eles podem ter alguma interface de serialização. Adicionamos mais funções que permitem que a instância da classe armazene seu estado em armazenamento e recupere seu estado do armazenamento. Isso mantém o encapsulamento porque a instância da classe ainda está no controle de seus próprios dados. Podemos estar serializando dados privados, mas o restante do programa não tem acesso a eles (ou, mais precisamente, mantemos um muro chinês escolhendo não corromper deliberadamente esses dados privados), e a instância da classe pode (e deve) faça verificações de integridade na desserialização para garantir que seus dados voltem bem.
Às vezes, os dados precisam de verificações de alcance, de integridade ou coisas assim. Escrever essas funções por nós mesmos nos permite fazer tudo isso. Nesse caso, não queremos ou precisamos do Lombok, porque estamos fazendo tudo isso sozinhos.
Porém, freqüentemente, você descobre que um parâmetro definido externamente é armazenado em uma única variável. Nesse caso, você precisaria de quatro funções para obter / configurar / serializar / desserializar o conteúdo dessa variável. Escrever você mesmo essas quatro funções sempre que você o atrasa e é propenso a erros. A automação do processo com o Lombok acelera o seu desenvolvimento e elimina a possibilidade de erros.
Sim, seria possível tornar pública essa variável. Nesta versão específica do código, seria funcionalmente idêntico. Mas volte ao motivo pelo qual estamos usando funções: "Podemos mudar a maneira como a classe faz coisas sob a superfície ..." Se você tornar sua variável pública, estará restringindo seu código agora e para sempre a ter essa variável pública como a interface. No entanto, se você usar funções ou usar o Lombok para gerar automaticamente essas funções, poderá alterar os dados subjacentes e a implementação subjacente a qualquer momento no futuro.
Isso torna mais claro?
fonte
Na verdade, não sou desenvolvedor de Java; mas o seguinte é praticamente independente de plataforma.
Quase tudo o que escrevemos usa getters e setters públicos que acessam variáveis privadas. A maioria dos getters e setters são triviais. Mas quando decidimos que o setter precisa recalcular algo ou o validador faz alguma validação ou precisamos encaminhar a propriedade para uma propriedade de uma variável membro dessa classe, isso é completamente ininterrupto para todo o código e é compatível com binários. pode trocar esse módulo.
Quando decidimos que essa propriedade realmente deve ser computada em tempo real, todo o código que olha para ela não precisa ser alterado e apenas o código que grava nela precisa ser alterado e o IDE pode encontrá-la para nós. Quando decidimos que é um campo computável gravável (só tivemos que fazer isso algumas vezes), também podemos fazer isso. O bom é que algumas dessas mudanças são compatíveis com binários (a mudança para o campo computado somente leitura não está na teoria, mas pode estar na prática de qualquer maneira).
Acabamos com muitos e muitos getters triviais com setters complicados. Também acabamos com alguns getters de cache. O resultado final é que você pode assumir que os getters são razoavelmente baratos, mas os setters podem não ser. Por outro lado, somos sábios o suficiente para decidir que os setters não persistem no disco.
Mas eu tive que rastrear o cara que estava mudando cegamente todas as variáveis de membro para propriedades. Ele não sabia o que era a adição atômica, então mudou a coisa que realmente precisava ser uma variável pública para uma propriedade e quebrou o código de maneira sutil.
fonte
Getters e setters são uma medida "just in case" adicionada para evitar a refatoração no futuro se a estrutura interna ou os requisitos de acesso mudarem durante o processo de desenvolvimento.
Digamos, alguns meses após o lançamento, seu cliente informa que um dos campos de uma classe às vezes é definido como valores negativos, mesmo que no máximo esse valor seja 0, nesse caso. Usando campos públicos, você precisaria procurar todas as atribuições desse campo em toda a sua base de código para aplicar a função de fixação ao valor que você definirá. Lembre-se de que sempre será necessário fazer isso ao modificar esse campo, mas isso é péssimo. Em vez disso, se você já estivesse usando getters e setter, seria capaz de modificar apenas o método setField () para garantir que esse grampo seja sempre aplicado.
Agora, o problema com linguagens "antiquadas" como Java é que elas incentivam o uso de métodos para esse fim, o que apenas torna seu código infinitamente mais detalhado. É difícil escrever e difícil de ler, e é por isso que usamos o IDE que atenua esse problema de uma maneira ou de outra. A maioria dos IDE gera automaticamente getters e setters para você e também os oculta, a menos que seja dito o contrário. O Lombok vai um passo além e simplesmente os gera processualmente durante o tempo de compilação para manter seu código ainda mais elegante. No entanto, outras linguagens mais modernas simplesmente resolveram esse problema de uma maneira ou de outra. Linguagens Scala ou .NET, por exemplo,
Por exemplo, no VB .NET ou C #, você poderia simplesmente fazer com que todos os campos tivessem campos simples, sem efeitos colaterais e setters, apenas campos públicos e, posteriormente, torná-los privados, alterar seu nome e expor uma propriedade com o nome anterior de o campo, onde você pode ajustar o comportamento de acesso do campo, se necessário. Com o Lombok, se você precisar ajustar o comportamento de um getter ou setter, basta remover essas tags quando necessário e codificar as suas com os novos requisitos, sabendo com certeza que você não precisará refatorar nada em outros arquivos.
Basicamente, a maneira como seu método acessa um campo deve ser transparente e uniforme. As linguagens modernas permitem definir "métodos" com a mesma sintaxe de acesso / chamada de um campo, para que essas modificações possam ser feitas sob demanda sem pensar muito nisso durante o desenvolvimento inicial, mas o Java força você a fazer esse trabalho com antecedência, porque não possui esse recurso. Tudo o que o Lombok faz é economizar algum tempo, porque o idioma que você está usando não deseja economizar tempo para um método "just in case".
fonte
foo.bar
mas pode ser manipulada por uma chamada de método. Você alega que isso é superior à maneira "antiquada" de ter uma API parecida com chamadas de métodofoo.getBar()
. Parece que concordamos que os campos públicos são problemáticos, mas afirmo que a alternativa "antiquada" é superior à alternativa "moderna", pois toda a API é simétrica (todas as chamadas de método). Na abordagem "moderna", temos que decidir quais coisas devem ser propriedades e quais devem ser métodos, o que complica tudo (especialmente se usarmos a reflexão!).Sim, é contraditório. Eu primeiro encontrei propriedades no Visual Basic. Até então, em outros idiomas, minha experiência, não havia invólucros de propriedades em torno dos campos. Apenas campos públicos, privados e protegidos.
Propriedades era um tipo de encapsulamento. Entendi que as propriedades do Visual Basic eram uma maneira de controlar e manipular a saída de um ou mais campos, ocultando o campo explícito e até mesmo seu tipo de dados, por exemplo, emitindo uma data como uma string em um formato específico. Mas, mesmo assim, não se está "ocultando o estado e expondo a funcionalidade" da perspectiva maior do objeto.
Mas as propriedades se justificam por causa de getters e setters de propriedades separados. Expor um campo sem propriedade era tudo ou nada - se você pudesse lê-lo, poderia alterá-lo. Portanto, agora é possível justificar um design de classe fraco com setters protegidos.
Então, por que nós / eles não usamos apenas métodos reais? Porque era Visual Basic (e VBScript ) (oooh! Aaah!), Codificando para as massas (!), E era toda a raiva. E assim uma idiotocracia acabou dominando.
fonte