Feature Envy

Feature EnvyInveja é bom? Claro que não! Inveja é um dos sete pecados capitais! Agora, imagine se houver inveja entre as classes de um programa? Ops, isso significa que, neste caso, o desenvolvedor está cometendo um pecado capital no seu código? Faz sentido!

Acompanhe o artigo e entenda melhor o que é uma Feature Envy!

 

Bom, já sabemos que o Clean Code nos ensinar a praticar a expressividade e legibilidade no nosso código, certo? Porém, além disso, é importante considerar que a prática do Clean Code também evita que o nosso código “cheire mal”. Este termo, “Code Smells”, é mencionado por Martin Fowler no seu livro “Refactoring: Improving the Design of Existing Code”. Segundo o autor, há porções de código que, só de olhar, o desenvolvedor faz careta, como se estivesse sentindo um cheiro ruim. Você deve ter se identificado com uma situação dessas, não? 🙂

Feature Envy é um dos elementos que causam esse “cheiro” e ocorre quando uma classe começa a utilizar, em excesso, as propriedades internas de outra classe. Em outras palavras, a classe não se limita a utilizar as propriedades que possui e acaba por utilizar propriedades externas. Na definição de Uncle Bob, é como se uma classe “desejasse” ser outra.
Para explicar melhor, um exemplo prático! No código abaixo, provavelmente você já vai identificar os pontos em que há inveja:

procedure ProcessarProduto(const Produto: TItemVenda);
var
  Descricao: string;
  Qtde: integer;
  Preco, Desconto, Total: real;
begin
  // obtém os dados do item que vem como parâmetro
  Descricao := Trim(UpperCase(Produto.Descricao));
  Qtde := Produto.Qtde;
  Preco := Produto.Preco;
  Desconto := Produto.Desconto;
 
  // calcula o total com o desconto
  Total := Qtde * Preco;
  Total := Total - (Total * Desconto / 100);
 
  // incrementa o número de vendas do produto
  Produto.NumVendas := Produto.NumVendas + 1;
 
  // exibe os dados na tela para o usuário
  Edit1.Text := Descricao;
  Edit2.Text := FloatToStr(Total);
end;

Embora seja um exemplo bem básico, você notou a quantidade de vezes que as propriedades do objeto “Produto” são acessadas? O método ProcessarProduto obtém a descrição, quantidade, preço e desconto do item, além de, ainda por cima, incrementar o número de vezes que aquele produto foi vendido. Agora você fez careta, não é? 🙂
O código acima é um exemplo típico de Feature Envy. O método ProcessarProduto inveja o escopo da classe TItemVenda, como se ele desejasse estar dentro dela. Além da má impressão que o código apresenta, a Feature Envy também pode elevar o acoplamento entre classes e causar dependências desnecessárias, portanto, é muito provável que outros problemas possam surgir a partir dessa “inveja”.

 

Para evitar o Feature Envy, aliás, para educar o código e ensiná-lo a não ter inveja (haha), basta trabalhar adequadamente com encapsulamento.
Na solução abaixo, observe como o código fica mais limpo e, claro, menos invejoso:

procedure ProcessarProduto(const Produto: TItemVenda);
var
  Descricao: string;
  Total: real;
begin
  Descricao := Produto.ObterDescricaoItem;
  Total := Produto.ObterTotalItem;
  Produto.IncrementarNumeroDeVendas;
 
  Edit1.Text := Descricao;
  Edit2.Text := FloatToStr(Total);
end;

Na classe TItemVenda, essa seria a implementação dos métodos de acesso:

function TItemVenda.ObterDescricaoItem: string;
begin
  result := Trim(UpperCase(FDescricao));
end;
 
function TItemVenda.ObterTotalItem: real;
var
  Total: real;
begin
  Total := FQtde * FPreco;
  Total := Total - (Total * FDesconto / 100);
 
  result := Total;
end;
 
procedure TItemVenda.IncrementarNumeroDeVendas;
begin
  Inc(FNumVendas);
end;

 

Mas, no método ProcessarProduto, você continua acessando a classe TItemVenda!
Sim, mas, ao invés de acessar diretamente as propriedades, estou acessando métodos encapsulados públicos, que são disponibilizados justamente para retornar valores internos. Na verdade, esse é um dos propósitos do encapsulamento da Orientação a Objetos. Não conhecemos as propriedades e nem os cálculos que ocorrem dentro do método ObterTotalItem, mas sabemos que ele irá nos retornar o total do item da venda.

Só mais uma observação: o incremento do número de vendas poderia ser mais confiável. Ao invés de um método, uma alternativa é criar uma Trigger no banco de dados para automatizar essa operação. Dessa forma, o número de vendas seria incrementado somente quando o item fosse efetivamente gravado no banco de dados, aumentando a confiabilidade e eliminando algumas linhas de código na aplicação.

 

Fico por aqui, pessoal.
Até a próxima semana!


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.