Por que (não) precisa de getters?

O artigo anterior sobre setters / getters como uma maneira de trabalhar com uma entidade (usando o Symfony no PHP como exemplo) recebeu uma discussão acalorada. Neste artigo, tentarei expressar meus pensamentos separadamente sobre getters: por que e quando obter algo, qual responsabilidade eles decidem e quando é apropriado usá-los, quando não é apropriado. Tentei reunir pensamentos em um só lugar e formalizá-los com você.

getHumanFactory (). getHuman (). getDoneWork (). getWorkTime ()


Os getters são necessários para obter algum estado do objeto atual. Nos idiomas OOP, esse é o valor de alguma variável de classe, geralmente privada.

class Article {
     private String name

     public function getName(): String {
          return this.name
     }
}

O valor da variável de classe em si pode ser obtido conforme desejado. Pedimos apenas ao objeto que nos dê o que é, o próprio nome do método nos diz "dê": mas não "faça", não "envie", não "crie", não "crie", não "conte". Diz dar - dê o que é. Isso funciona normalmente em todos os tipos de objetos de dados, cuja responsabilidade é apenas fornecer / transportar informações.

Não pode haver lógica no getter, porque a semântica da palavra é direta. "Mãe, me dê uma torta " não contém "Mãe, compre farinha, atire uma torta e, finalmente, me dê uma torta ". Talvez a frase "forneça uma torta" de alguma forma possa encapsular todas essas ações, mas definitivamente não é "dar".

Você ficará surpreso, mas conheci essa maneira de nomear todos os métodos mais de uma vez. A complexidade do código ao mesmo tempo cresce bastante, porque nas cadeias getter nem sempre é claro o que exatamente acontece nas camadas do aplicativo e que tipo de conexões estão envolvidas, como regra, nem tudo está em ordem com o design e a conivência desse tipo é uma fonte de erros.

getValue () throw WhyDoYouNeedIt? {}


O objetivo do objeto de dados é compreensível - este é o cabo do filme de 1917, que está fugindo para algum lugar para transmitir alguma mensagem sobre a necessidade de recuar.

Mas o que fazer com objetos de negócios? Por que a entidade "Documento" fornece a alguém uma lista de seus campos?

Se este for um objeto de negócios, o documento poderá ser lançado, rejeitado, verificado (inclusive nesses campos), preenchido ou confirmado.

Se for necessário "fornecer" o campo, este documento não faz parte do processo de negócios. Por que preciso fornecer um campo a alguém? Talvez para publicação? Ou extrair, arquivar ou enviar uma cópia por correio ou relatório? Não está totalmente claro por que e para quem - há uma responsabilidade separada de "dar", como a de um bom objeto de dados antigo, você pode ver claramente o uso na forma de alguma fonte para leitura no contexto de outro processo de negócios que não é básico para entender o próprio documento.

doEverythingEverywhere (world.getGod ())


Voltar para as entidades de negócios. Se você deixar os getters em entidades, é grande a tentação de usá-los exatamente em todos os lugares e como quiser. Estamos ligados ao estado de um determinado objeto e revertemos absolutamente qualquer lógica para a campanha. Pode até parecer que não quebramos o encapsulamento. Mas que tal? Estado em um lugar, comportamento em outro - uma violação clássica do encapsulamento.

Por exemplo, há alguma entidade:

class Order
{
     private Status status
     private Boolean closed

     public function getStatus(): Status {
          return this.status
     }

     public function setClosed(Boolean closed): void {
           this.closed = closed
     }
}

Certamente, com essa arquitetura, você solicitará um status em uma dúzia / cem lugares. Pode ser de todos os tipos de serviços, controladores e outros módulos. Vi apenas uma vez quando restrições foram criadas artificialmente para a distribuição desse código por algum conjunto de regras para desenvolvedores, não por código ... O encapsulamento foi fornecido pelos padrões de codificação e não pelo design do código :).

Se você precisava fazer algo assim em algum lugar:

if(order.getStatus() == input.getStatus()) {
      order.setClosed(true)
}

É provável que a entidade não contenha uma máquina de estado. Isso significa que os invariantes do objeto por dentro não são controlados de forma alguma - não há verificação de dados durante cada operação a partir do interior. Como resultado - alta conectividade, teste funcional complexo, uma vez que não é suficiente verificar a lógica da unidade do código que altera o estado da entidade, é necessário verificar se o estado da entidade externa ao código é válido. E como resultado: maior complexidade, probabilidade de erros, mais código e testes complexos.

Conclusão: espero que meus colegas e seus colegas usem getters menos como qualquer método que funcione com o resultado que retornará.

E espero que mais desenvolvedores prestem atenção ao conceito do CQRS, onde as responsabilidades pelas operações de leitura e negócios estão divididas.

Bom para todos!

All Articles