terça-feira, 2 de maio de 2017

Autoencapsulamento – quando o esforço é válido?

Por Martin Fowler.
Em 25/04/2017 no site iMasters.

O encapsulamento de dados é um princípio central na orientação a objetos. Isso quer dizer que os campos de um objeto não devem ser expostos publicamente. Em vez disso, todos os acessos de fora do objeto deveriam ser via métodos de acesso (getters e setters). Existem linguagens que permitem campos acessíveis ao público, mas geralmente advertimos que os programadores não façam isso. Autoencapsulamento vai um passo adiante, indicando que todo o acesso interno a um campo de dados também deve passar por métodos de acesso. Somente os métodos de acesso devem tocar os dados. Se o campo de dados não estiver exposto ao exterior, isso significará adicionar acessores privados adicionais.
Aqui está um exemplo de uma classe Java razoavelmente encapsulada:
Classe charge
1private int units;
2private double rate;
3 
4public Charge(int units, double rate) {
5  this.units = units;
6  this.rate = rate;
7}
8public int getUnits() { return units; }
9public Money getAmount() { return Money.usd(units * rate); }
Ambos os campos são imutáveis. O campo de unidades é exposto a clientes da classe por meio de um getter, mas o campo de taxa é usado apenas internamente. Portanto, não precisa de um getter.
Aqui está uma versão usando autoencapsulamento:
Classe ChargeSE
1private int units;
2private double rate;
3 
4public ChargeSE(int units, double rate) {
5  this.units = units;
6  this.rate = rate;
7}
8public int getUnits()    { return units; }
9private double getRate() { return rate; }
10public Money getAmount() { return Money.usd(getUnits() * getRate()); }
Autoencapsulamento significa que getAmount precisa acessar ambos os campos através de getters. Isso também significa que eu tenho que adicionar um getter para rate, que eu deveria fazer privado.
Encapsular dados mutáveis geralmente é uma boa ideia. Funções de atualização podem conter código para executar validações e lógica consequente. Ao restringir o acesso através de funções, podemos apoiar o UniformAccessPrinciple, o que nos permite esconder quais dados são computados e quais são armazenados. Esses acessores nos permitem modificar as estruturas de dados, mantendo a mesma interface pública. As linguagens diferem em detalhes do que é “externo” para um objeto por vários tipos de AccessModifier, mas a maioria dos ambientes suporta encapsulamento de dados até certo ponto.
Eu passei por algumas organizações que trabalhavam com autoencapsulamento obrigatório e usá-lo ou não era um tema regular de debate desde a década de 90. Seus defensores disseram que o encapsulamento foi um benefício, que você gostaria de incorporá-lo ao acesso interno também. Os críticos argumentaram que foi uma cerimônia desnecessária que levou a um código desnecessário que obscureceu o que estava acontecendo.
Minha opinião sobre isso é que na maioria das vezes há pouco valor no autoencapsulamento. O valor do encapsulamento é proporcional ao escopo do acesso a dados. Classes são geralmente pequenas (pelo menos as minhas são), então acesso direto não será um problema dentro desse escopo. A maioria dos acessores são atribuições simples para o setter e de recuperação para o getter, por isso há pouco valor em usá-los internamente.
Mas há circunstâncias comuns em que o autoencapsulamento vale o esforço. Se houver lógica no setter, então é aconselhável considerá-lo para todas as atualizações internas também. Outra circunstância é quando a classe é parte de uma estrutura de herança, caso em que os acessores fornecem valiosos pontos de hook para subclasses para substituir o comportamento.
Portanto, o meu primeiro passo usual é usar o acesso direto aos campos, mas refatorar usando Campo de autoencapsulamento se as circunstâncias o exigirem. Muitas vezes, as forças que me levam a considerar autoencapsulamento eu posso resolver, extraindo uma nova classe.

Nenhum comentário:

Postar um comentário