Como desenvolvedor, penso que uma das competências necessárias é saber refatorar o código. Isto se aprende com o tempo, e ler ajuda bastante, li o livroRefatoração: Aperfeiçoando o projeto de código existente, do Martin, e ele é muito bom, claro e coeso, demonstrando algumas refatorações consolidadas.
link livro: http://www.submarino.com.br/produto/1/242126/refatoracao:+aperfeicoando+o+projeto+de+codigo+existente
Então, como tenho um tempo livre, resolvi compartilhar algumas partes e expor para discussão aqui na lista.
Primeiro, quando refatorar?
Pode ser ...
- Quando corrige um erro;
- Quando se está adicionando uma nova funcionalidade;
- Quando está se revisando o código;
Ou, como diz Martin, na edição de seu código, na terceira vez que tiver que mexer em algo, refatore.
Segundo, é necessário refatorar?
Depende, Kent Beck diz que deve-se refatorar quando detectar mau cheiro no código. Mau cheiro, não quer dizer que algo não funcione, mas sim aquele código em que tu olhas atravessado e pensa "Dá para melhorar isso".
Abaixo, uma lista resumida de alguns maus cheiros clássicos:
- Nomes obscuros de variáveis;
- Código duplicado;
- Método muito longo;
- Classe muito longa;
- Muitos parâmetros no método;
- Construtor com nulos;
- Muitas variáveis de instância;
- Campos temporários;
- Comentários no código (a.k.a. "desodorante")
Um dos princípios da refatoração é deixar o código legível e por consequência mais manutenível. Uma refatoração que aplico com frequência, é o extrair método, que uso para substituir comentários ou reusar parte de um método para outros métodos, e alterar o nome de variáveis.
Inventei uma classe 'mock' (hehehe), por exemplo:
class PagamentoService {
private ContabilService cs;
public void encaminhaPagamentosCartao(Integer id) {
if (id == null || id <= 0) throw new IllegalArgumentException("ID invalido");
Venda e = em.find(Venda.class, id);
if (venda == null) throw new BusinessException("Venda nao encontrada para o ID:" + id);
List pc = new ArrayList();
for (Item i : e.itens) {
boolean quitado = true;
for (Pagamento p : i.pagamentos) {
if (!p.faturado) quitado = false;
}
if (quitado) {
for (Pagamento p : i.pagamentos) {
if (p.isCartao) {
pc.add(p);
}
}
}
}
for (Pagamento p : pc) {
cs.send(p);
}
}
}
Para ficar mais legível, poderíamos deixá-la assim:
class PagamentoService {
private ContabilService contabilService;
public void encaminhaPagamentosCartao(Integer idVenda) {
validaParametro(idVenda);
Venda venda = localizaVenda(idVenda);
enviaPagamentoParaContabilidade(venda);
}
private void validaParametro(Integer idVenda) {
if (idVenda == null || idVenda <= 0) throw new IllegalArgumentException("ID da Venda é invalido");
}
private Venda localizaVenda(Integet idVenda) {
Venda e = em.find(Venda.class, id);
if (venda == null) throw new BusinessException("Venda nao encontrada para o ID:" + id);
}
private void enviaPagamentoParaContabilidade(Venda venda) {
for (Item item : venda.itens) {
if (estaQuitado(item)) {
for (Pagamento pagamento : item.pagamentos) {
if (pagamento.isCartao) {
enviaParaContabilidade(pagamento);
}
}
}
}
}
private boolean estaQuitado(Item item) {
for (Pagamento pagamento : item.pagamentos) {
if (!pagamento.faturado) return false;
}
return true;
}
private void enviaPagamento(Pagamento pagamento) {
contabilService.send(pagamento);
}
}
Geralmente a refatoração adiciona indireção, obviamente, mas vai ao encontro do paradigma orientado a objetos, que prega a utilização de pequenas partes reutilizáveis de código, sempre pensando no futuro. Neste exemplo, os comentários foram substituídos por métodos (extrair método) que falam por si, as variáveis foram renomeadas e uma delas trocada por uma consulta (substituir variável temporária por consulta).
Como benefício, futuros métodos públicos podem utilizar os métodos privados para operações semelhantes.
O difícil é saber até onde refatorar, dividir em pequenas peças, mas isto vem com a experiência. Após acostumar-se a refatorar, passa a não se ter mais medo de alterar o código, mas deve ser feito com cuidado para não introduzir bug's, e aí entra a importância dos testes. Geralmente a refatoração é associada com alguma prática de TDD.
Em outro tópico lanço outro estudo de caso e refatorações.
Para os analistas e DBA's de plantão, sugiro este ótimo livro: http://www.agiledata.org/essays/databaseRefactoring.html
Li alguns pequenos trechos, já que estou mais ativo no código, e acho que vale a pena.