Monthly Archives: July 2010

Um código limpo diz mais que mil comentários

Você comenta seu código? Sim? Por quê?

Cuidado! Comentários podem ser perigosos! O uso deles pode ser um indício de que seu código está confuso e você tem medo de precisar alterar algo mais tarde e esquecer o que aquela variável aux armazena.

A forma mais elegante de se remover comentários é refatorando. Aux é um nome sem sentido, você pode até lembrar o que essa variável significa enquanto você ainda está programando, mas daqui duas semanas você já vai ter esquecido. Nomeie melhor suas variáveis, não tenha medo de ter uma variável assim: listaDeClientesInativos ao invés de lista.

Outro caso bastante comum de comentários é quando você tem aquele método gigante com 100 linhas e a cada 10 você resolve colocar um comentário para dizer o que está acontecendo. Nestes casos você pode (e deve) utilizar extract method. Divida seu grande método em outros pequenos, assim você pode substituir os comentários por métodos com nome auto-explicativos, além disso estará de acordo com o Single responsibility principle.

Não é possível garantir que outras pessoas do seu time irão atualizar o comentário quando alguma alteração for feita. Pior do que ter um método cheio de comentário é ter um método com um comentário obsoleto. Além disso, a leitura do comentário é opcional, você não tem como saber se seu comentário será lido ou não. Se você tem algo importante para dizer, diga no seu código, através de nomes bem definidos e código limpo.

Dito tudo isso, não preciso nem comentar sobre o exemplo abaixo né?

//Chamado #213: Corrigido bug onde o desconto final estava sendo somado ao invés de descontar.
int total = pedido.getTotal() - descontoFinal;

Se você faz isso, sugiro procurar algum software de controle de versão.

Algum tempo atrás escrevi sobre Expressões Regulares (aqui) e falei sobre comentários em Regex pattern. Continuo achando uma forma elegante de se utilizar comentários já que um pattern quase nunca é limpo, é sempre complicado ler aquilo e entender. Os comentários ajudam neste caso tornando-o mais legível. Outra opção seria escrever testes automatizados para seu Regex. Além dos testes servirem como exemplos de uso, você estará tendo uma maior cobertura do seu código e a garantia de poder alterar o pattern sem medo de inserir um novo bug.

Falando em Regex e Testes, lembrei deste tweet do @gchapiewski.

It’s impossible to program complex regular expressions without TDD. Period.

Resumindo. Mais código limpo e menos comentários. Ah, não esqueça de ler o livro Clean Code. :)

Evitando o excesso de ‘null check’ (continuação)

Conforme comentado no outro post, vou descrever aqui outra forma de evitar a verificação de um objeto nulo. Este tipo de verificação que é tão usada causa um bad smell tremendo.

Imagine a seguinte funcionalidade de um sistema de vendas e-commerce:

Quando nós enviarmos um pedido para um cliente que comprou em nossa loja, nós queremos que este cliente seja avisado que seu produto já foi deixado nos correios. Gostariamos de enviar um SMS para os clientes que consideramos ótimo e um e-mail para aqueles que achamos que são bons clientes. Qualquer outro cliente não deve receber o aviso, se ele tiver interesse ele passa no site e verifica o status.

Então, modelando esta user story, podemos ter uma interface chamada de Notificador e algumas implementações como NotificadorViaSms e NotificadorViaEmail. Isso nos permite que em um dado momento possamos incluir algum outro tipo de notificador, por exemplo, NotificadorViaTwitter.

Pois bem, tendo múltiplas implementações eu vou precisar de um factory que vai ser responsável por criar um notificador baseado no tipo do cliente. Aqui vai um exemplo do código:

public class NotificadorFactory {
	public static Notificador cria(Pedido pedido) {
		if (pedido.getCliente().getTipo() == TipoCliente.Otimo)
			return new NotificadorPorSms(pedido);
		else if (pedido.getCliente().getTipo() == TipoCliente.Bom)
			return new NotificadorPorEmail(pedido);
		else
			return null;
	}
}

Seu uso ficaria assim:

Cliente maria = new Cliente("Maria", TipoCliente.Otimo);
Pedido pedido2 = new Pedido(maria);
pedido2.setTotal(520.10f);

Notificador notificador = NotificadorFactory.cria(pedido2);
notificador.enviar();

Então, o que acharam? Ficou legal, não? Eu gostei :) (só não gostei do nome da factory e do seu método, mas não é esse o objetivo).

Só que este código tem um problema. E se a cliente “maria” fosse do tipo Ruim ao invés de Otimo? Segundo a minha user story, eu não deveria avisar ela. Minha factory está correta, se o cliente for do tipo Ruim ela não vai retornar nenhum Notificador, vai retornar null. Quando meu código cliente for chamar o método enviar() vai surgir um NullPointerException.

Solução: coloca um “if notificador != null” antes de chamar o método. Yeah! Resolvemos o problema, mas criamos outro… Em todo lugar que você for usar o NotificadorFactory você vai ter que se lembrar de que ele pode retornar um valor nulo, e por isso será necessário verificar se o retorno é nulo na maioria das chamadas.

Outra solução seria aplicar o Null Object Pattern. Segundo a definição encontrada no Wikipedia:

Original:

In object-oriented computer programming, a Null Object is an object with defined neutral (“null”) behavior. The Null Object design pattern describes the uses of such objects and their behavior (or lack thereof).

Traduzido:

Em programação orientada a objetos, um Objeto Nulo é um objeto que possui comportamento neutro (nulo). O padrão de projeto Null Object descreve o uso de tais objetos e seu comportamento (ou a falta dela).

De forma simplificada, o Objeto Nulo segundo o padrão de projeto é um objeto que não faz nada, que possui um comportamento neutro. Em nosso caso poderiamos ter um notificador chamado NotificadorVazio que seria criado pelo NotificadorFactory ao invés de retornar null. Sendo assim, o código cliente ficaria livre para utilizar o retorno da factory sem se preocupar com NullPointerException pois o retorno nunca seria nulo. Curioso para saber como fica a implementação do NotificadorVazio? Olha que simples:

A Interface:

public interface Notificador {
	void enviar();
}

A implementação nula:

public class NotificadorVazio implements Notificador {
	@Override
	public void enviar() {
		//Não fazer nada
	}
}

A factory:

public class NotificadorFactory {
	public static Notificador cria(Pedido pedido) {
		if (pedido.getCliente().getTipo() == TipoCliente.Otimo)
			return new NotificadorPorSms(pedido);
		else if (pedido.getCliente().getTipo() == TipoCliente.Bom)
			return new NotificadorPorEmail(pedido);
		else
			return new NotificadorVazio();
	}
}

Pronto, resolvemos o problema de forma organizada, orientada a objetos e utilizando padrões bem definidos.

Até mais :D

Sharing Buttons by Linksku