quinta-feira, 29 de setembro de 2016

Tipos de Code Smells


Fala galera que acompanha o blog...

Antes de continuar lendo esse post sobre os Tipos de Code Smells, eu sugiro que caso não saiba o que são Code Smells, leia o post que fizemos sobre o que são Code Smells.

Bom, com o paradigma de orientação a objetos existem duas categorias de code smells: Within Classes (dentro de classes) e Between Classes (Entre classes).



Tipos de Code Smells


Code Smells within classes



Comments:
     É evidenciado pelo excesso de comentários que não necessariamente precisariam ser escritos e, ao invés de auxiliar, acabam por prejudicar o entendimento do código-fonte. Sempre atentar para que as explicações deixadas nos comentários e ter em mente que o comentário está sendo escrito para as pessoas, e não para as máquinas.

Long Method:
    Não importa qual o paradigma escolhido, mas saiba-se que métodos, funções ou procedimentos extremamente longos são difíceis de entender. Quanto maiores eles são e quanto mais parâmetros e variáveis eles usam, os tornam mais propensos a fazer mais do que seu nome sugere. Métodos curtos são mais “entendíveis” em relação ao que o seu código-fonte faz, permite um maior reuso de código e mais flexibilidade. Claro que, com métodos mais curtos há mais elementos para cuidar, e ter mais chamadas aos métodos curtos significa mais vezes tendo que verificar o que o método chamado realmente faz. Entretanto, se é dado aos métodos bons nomes, que mostram quais os seus propósitos, elimina-se muito a necessidade de olhar para o corpo dos métodos e a necessidade de adicionar comentários para esclarecer o código-fonte.

Too many parameters (Long Parameter List):
     Em programação orientada por objetos, a maioria dos dados necessários a um método pode ser diretamente obtida a partir dos próprios objetos, caso eles sejam visíveis para o método, ou eles podem ser derivados através de um pedido de outro parâmetro. Portanto, as listas de parâmetros devem ser curtas, pois quanto mais extensas se tornam mais difíceis de ler, difícil de usar e elas mudam muito quando mais dados são necessários. Fazer uma mudança para uma lista de parâmetros significa mudar todas as referências ao método envolvido. E assim, mantê-lo curto.

Duplicated Code:
     É caracterizado quando um código idêntico ou muito semelhante existe em mais de um local. Deve-se atentar para eliminar toda e qualquer duplicação sempre que possível. Procurando sempre casos mais sutis de quase duplicação no código-fonte escrito.

Large Class:
    As classes grandes são caracterizadas por deterem muitas responsabilidades, com muitos dados e/ou métodos demais. O que torna isto um problema, porque essas classes são difíceis de manter e compreender devido ao seu tamanho. As classes grandes muitas vezes recai nas situações que exemplificam “Duplicated Code” ou “Shotgun Surgery”. A solução é encontrar partes de dados e métodos que parecem estar bem relacionados e refatorar utilizando Extract Class.

Type Embedded in Name:
     Situação onde se coloca nomes de tipos em nomes de métodos. Isto não é apenas redundante, mas o obriga a mudar o nome, se as mudanças de tipo se fizerem necessárias, ou numa opção pior ainda: nome “tipado” divergente do tipo definido permanece inalterado no código-fonte.

Uncommunicative Name:
     Ocorre quando se opta por colocar nomes em métodos que não descreve sucintamente o que ele faz. O ideal é que os métodos procurem sempre passar de forma simplista, a função que o mesmo detém, apenas no nome que lhe é dado. Fazendo isso, evita-se no futuro em uma refatoração ter que renomeá-lo ou reescrevê-lo.

Inconsistent Names:
     Comumente acontece quando não se atenta para o conjunto de nomes dados aos métodos, de uma forma mais ampla, e só se detém a uma escolha que descreve minimamente tal método, sem transparecer a correlação com que ele tem com os demais criados. Para resolver isso, basta na hora da escolha optar por um conjunto de terminologia padrão e cumpri-lo em todos os seus métodos. Por exemplo, se existe Open(), provavelmente deve ter Close().

Dead Code:
     Acontece quando o desenvolvedor não elimina trechos de código-fonte que não está sendo utilizado pelo sistema, pensando que o mesmo poderá ser preciso futuramente. A boa prática prega que se deve apagar tal código-fonte que não está sendo usado e, se necessário, obtê-lo a partir do controle de versão do projeto.

Speculative Generality:
     Fica explicito quando se escreve código-fonte que não atende nenhum propósito no momento e se pensa em expandi-lo para efetivamente representar algo no sistema apenas no futuro. Para isso deve-se manter o foco e apenas escrever código para resolver os problemas de hoje, não se preocupando com os problemas de amanhã, quando realmente se concretizar. Atenção para isso: Não quer dizer que a abordagem é escrever código-fonte sem pensar no uso futuro dele, pois certo é escrever o que precisa hoje de modo a ser expansível, e errado é criar “esqueletos” de código-fonte inacabados (ou até acabados mas sem ligação alguma) e que existe uma definição real de que o mesmo será utilizado um dia.

Conditional Complexity:
     Lógica condicional é relativamente fácil de ser compreendida quando bem aplicada e contida dentro de algumas linhas de código-fonte. Infelizmente, isso raramente acontece. Ocorre geralmente quando são implementados vários recursos novos e de repente a lógica condicional torna-se bastante complexa e confusa. Portanto deve-se ter cautela com os grandes blocos lógicos condicionais, particularmente blocos que tendem a crescer ou mudar significativamente ao longo do tempo.

Combinatorial Explosion:
     Trata de uma forma sutil de duplicação. Ela existe quando há vários trechos de código-fonte que fazem a mesma coisa usando diferentes tipos ou quantidades de dados ou objetos. Por exemplo: existem vários métodos em uma classe para executar consultas. Cada um destes métodos executa uma consulta usando condições específicas, podendo o desenvolvedor recair numa condição de explosão de métodos ao criar inúmeros deles para lidar com as muitas maneiras de realizar consultas.

Oddball Solution:
     É caracterizado quando um dado problema é resolvido de duas maneiras diferentes no mesmo sistema, uma das soluções é tida como a excêntrica ou solução inconsistente. A presença dessa constatação geralmente indica sutilmente código duplicado. Para remover essa duplicação, primeiro determine a maneira preferida, e depois pode se aplicar o “Substitute Algorithm” para produzir uma solução consistente em todo o sistema. Dada uma solução consistente, É possível mover todas as instâncias da solução para um lugar, eliminando assim a duplicação.

Temporary Field:
     É evidenciado quando a variável está no escopo da classe, quando deveria estar no escopo do método. Violando o princípio ocultação de informação. Por meio do Extract Class é possível sanar tal situação.

Code Smells between classes

Alternative Classes with Different Interfaces:
     Classes similares por dentro, mas diferente por fora. Elas podem ser modificadas para partilhar uma interface comum.

Primitive Obsession:
    Usar muitas variáveis primitivas para representar tipos complexos. Se o tipo de dados é bastante complexo, escreve-se uma classe para representá-lo.

Data Class:
     Usar classes só para conter dados. Classes com campos, getters e setters e nada mais.

Data Clumps (Aglomerados de dados):
    Os mesmos dados sendo manipulados em diferentes níveis podem estar relacionados. Considera-se colocar os dados relacionados acima em uma classe maior.

Refused Bequest:
     Herda mas não usa nada, então não se faz necessário a herança, pois está apenas contribuindo para o alto acoplamento.

Inappropriate Intimacy:
     Comportamento de uma classe influenciado por aspectos internos de outra classe. As classes deve saber o mínimo possível sobre de uma sobre a outra.

Indecent Exposure:
     Demasiado uso de public.

Feature Envy:
     Método que usa muito, mas muito mesmo, uma outra classe. Em geral, tenta-se colocar um método na classe que contém a maioria dos dados que o método necessita.

Lazy Class:
     Classe que não atende a si mesma e joga parte de sua responsabilidade para outras.

Message Chains:
     Métodos com muita execução ou variáveis temporárias para dados de rotina.

Middle Man:
     Classes delegando toda sua responsabilidade para outras.

Divergent Change:
     Uma alteração pontal afeta aspectos completamente diferentes.

Shotgun Surgery:
    Oposto da mudança divergente, pois para uma alteração pontal tem que mexer em várias classes.

Parallel Inheritance Hierarchies:
     Ao herdar uma classe tem que criar uma subclasse similar à subclasse presente na herdada.

Incomplete Library Class:
     Não é possível alterar a biblioteca e o método acaba indo para uma classe qualquer.

Solution Sprawl:
     Se precisar de 5 ou mais classes para fazer algo útil, precisa simplificar e consolidar.


Esta lista foi obtida a partir do arquivo  Smells to Refactorings PDF, e do Wiki Smells to Refactorings Wiki,, que também fornecem orientações adicionais sobre as refatorações específicas que podem ser úteis em cada caso.

Não fique encabulado com todos esses problemas que podem ser encontrados no código, é claro que um código limpo é sempre bem vindo, mas o mais importante é saber identificar esses mau cheiros do código e assim aplicar a refatoração adequada para cada caso.

É isso pessoal até a próxima!
Luiz Fagner Zordan Analista de Sistemas

Graduado em Sistemas de Informação, atualmente trabalhando na Embraer pela FocusNetworks como FullStack. Sou apaixonado por tecnologia, fascinado por jogos e adoro passar o tempo assistindo séries.