Mostrando postagens com marcador Encapsulamento. Mostrar todas as postagens
Mostrando postagens com marcador Encapsulamento. Mostrar todas as postagens

quarta-feira, 24 de maio de 2017

Um estudo quantitativo sobre o uso de herança e interface em sistemas Java Carlos Eduardo de Carvalho Dantas Universidade Federal de Uberlândia Faculdade de

Por Carlos Eduardo de Carvalho Dantas

Resumo

O recurso de herança é uma das principais características do paradigma de Orientação a Objetos. Contudo, trabalhos anteriores recomendam cuidado quanto ao seu uso, sugerindo alternativas em Java como a adoção de composição com implementação de interfaces. Apesar de ser um tema bem estudado, ainda há pouco conhecimento se estas recomendações foram amplamente adotadas pelos desenvolvedores de maneira geral. Este trabalho possui como objetivo avaliar como os recursos de herança e interface têm sido empregados em Java, comparando sistemas mais recentes com antigos, e também versões de cada sistema em épocas distintas. Os indicadores avaliados foram a quantidade de mudanças corretivas dos sistemas, quebras de encapsulamento pelo uso do operador instanceof, medidas de coesão, acoplamento e ocorrências de code smells. Por fim, foi realizada uma medição sobre o quão frequente os recursos de herança ou interface são adicionados ou removidos das classes, e as motivações pelas quais os desenvolvedores executam estes procedimentos. Foram analisados 1.656 sistemas open-source construídos entre 1997 e 2013, todos hospedados nos repositórios GitHub e SourceForge. Foi constatado que os desenvolvedores ainda utilizam herança primariamente como um recurso para reaproveitamento de código, motivados pela necessidade de evitar duplicidade de código-fonte. Em projetos mais recentes, as classes na hierarquia de herança apresentaram menos mudanças corretivas e as subclasses fizeram menos uso do operador instanceof. No entanto, à medida que evoluem, as classes na hierarquia de herança tendem a se tornar complexas na medida em que as mudanças ocorrem. As classes que implementam interfaces mostraram pouca relação com as suas respectivas interfaces implementadas, e foram observados indícios de que este recurso ainda é subutilizado. Estes resultados mostram que ainda existe alguma falta de conhecimento sobre o uso de práticas adequadas orientadas a objetos, o que reforça a necessidade de formação de desenvolvedores sobre como projetar melhores classes.

Palavras-chave: Herança. Interfaces. Mudanças corretivas. Code Smells. Encapsulamento. Coesão. Acoplamento. GitHub. SourceForge.

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.