Regras para construir bons métodos:
Método deve se concentrar em apenas uma tarefa:
Deve haver o mesmo nível de abstração para cada linha do método
Use nomes descritivos
Uma função com nome descritivo é melhor que um comentário descritivo.
Manter consistência com nomes de funções:
Funções descritivas não surpreendem (negativamente) o programador
Quanto menos argumentos, melhor
Funções não devem ter efeito colateral inesperado
Argumentos de saída
As funções devem ser de comando ou de consulta mas nunca ambos logo um método deve retornar informações sobre um objeto ou mudar o seu estado mas ambas as tarefas frequentemente levam a confusão.
Prefira exceções a retornar códigos de erro
Funções que retornam erros de código violam a regra de separação de comando e consulta pois estas fazem com que funções de comando sejam usados como funções de consulta. Finalmente, o maior problema é que o sistema que usa o método deve tratar o erro imediatamente no decorrer do fluxo.
Não repita blocos de código (no copy-paste)
Código duplicado é a origem de vários males e por isso, vários padrões de projeto e práticas foram criados com o propósito de eliminar a duplicação. Entretanto, às vezes não é tão fácil identificar a repetição de código e essa repetição é uma omissão que abre a oportunidade para que erros aconteçam.
- 1a regra: métodos devem ser pequenas
- 2a regra: métodos devem ser menores ainda
- Até 30 linhas. (Sugestão: deve caber na tela)
- Métodos devem fazer somente uma tarefa mas devem fazer isso bem.
- Antes: O método ObterLogradouros esta longo demais:
public virtual IList ObterLogradouros() { IList lstLogradouros = new ArrayList(); foreach(Logradouro logradouro in this.relLogradouros) { if (logradouro.GetType() != typeof(Logradouro)) { throw new SystemException("LogradouroVitoria se associa apenas com BairroVitoria."); } // Verifica se o logradouro ja foi adicionado na lista bool bAdicionado = false; for (int i=0; i < lstLogradouros.Count; i++) { if (((Logradouro)lstLogradouros[i]).Id == logradouro.Id) { bAdicionado = true; break; } } // Se nao foi adicionado na lista if (! bAdicionado) { lstLogradouros.Add(logradouro); } } return lstLogradouros; }
- Depois: o método ObterLogradouro se resume a menos linhas:
public virtual IListObterLogradouros() { IList lstLogradouros = new List (); foreach(Logradouro logradouro in this.relLogradouros) { EhLogradouroValidoParaBairro(logradouro); if (!lstLogradouros.Contains(logradouro)) { lstLogradouros.Add(logradouro); } } return lstLogradouros; }
Método deve se concentrar em apenas uma tarefa:
- Antes: Método faz várias tarefas:
public void autenticarLogin(Sessao sessao,string strSenha, Pessoa pessoaSelecionada,Sistema sistema) { if(strSenha == string.Empty) { throw new ApplicationException ("Favor preencher o campo senha!"); } if (pessoaSelecionada == null) { throw new ApplicationException ("Pessoa inexistente para o documento informado."); } Usuario usuarioPessoaSelecionada = DAOFactory.ObterDAOFactory() .ObterDAO(sessao).ObterUsuario(pessoaSelecionada); if(usuarioPessoaSelecionada == null) { string strErro = string.Empty; strErro += "A pessoa informada não possui permissão "; strErro += "para nenhum sistema!"; throw new ApplicationException(strErro); } if(!usuarioPessoaSelecionada.eAtivo()) { throw new ApplicationException("O usuário informado está desativado!"); } Projeto projeto = sistema.ObterProjeto(); if(!DAOFactory.ObterDAOFactory().ObterDAO (sessao) .ValidarSenhaProjeto(strSenha,projeto,usuarioPessoaSelecionada)) { throw new ApplicationException("Senha inválida!"); } AutenticarUsuario(usuarioPessoaSelecionada); }
- Depois: Método se concentra em apenas uma tarefa:
public void AutenticarUsuario(Sessao sessao,string strSenha, Pessoa pessoaSelecionada,Sistema sistema) { Usuario usuario = ObterUsuario(sessao,pessoaSelecionada,sistema); ValidarSenha(sessao,usuario,senha,sistema); }
Deve haver o mesmo nível de abstração para cada linha do método
- Antes: Níveis de abstração diferentes (checagem de strings)
public void AlteracaoObservacaoHistorico( Sessao sessao, string strObservacao, Historico historico) { if ((strObservacao == null) && (strObservacao == string.Empty)) { throw new ApplicationException("Observação do histórico deve ser informada."); } historico.AtribuirObservacao(strObservacao); sessao.BeginTransaction(); DAOFactory.ObterDAOFactory().Salvar(sessao,ref historico); sessao.CommitTransaction(); }
- Depois: Código com mesmo nível de abstração
public void AlteracaoObservacaoHistorico( Sessao sessao, string strObservacao, Historico historico) { historico.AtribuirObservacaoComChecagem(strObservacao); sessao.BeginTransaction(); DAOFactory.ObterDAOFactory().Salvar(sessao,ref historico); sessao.CommitTransaction(); }
Use nomes descritivos
Uma função com nome descritivo é melhor que um comentário descritivo.
- Antes - código não muito claro auxiliado por comentários
// Verifica se a pessoa é um funcionário aposentado if (funcionario.EstaAtivo()&&funcionario.ObterIdade()>65) { // Calculo o valor da sua aposentadoria valor = pessoa.CalculaValor(); }
- Depois - código claro com métodos descritivos
if (pessoa.EstaAposentado()) { valorAposentadoria = pessoa.CalculaValorAtualDaAposentadoria(); }
Manter consistência com nomes de funções:
pessoa = DAO.Factory.ObterDAO().RecuperarPeloNome("Joelson"); pessoa = DAO.Factory.ObterDAO ().RecuperarPeloCPF("08766563489"); pessoa = DAO.Factory.RecuperarPeloId (12); pessoa = DAO.Factory.RecuperarPelaMatricula(524567);
Funções descritivas não surpreendem (negativamente) o programador
Quanto menos argumentos, melhor
- O número ideal de argumentos para funções: 0 (ZERO), depois vem funções com 1 argumento, por último, funções com 2 argumentos
- Funções com 3 ou mais argumentos devem ser evitadas
- Quanto maior o número de argumentos, maior a possibilidade de confusão e erro
- Antes: Método com muitos argumentos
public void DesenhaRetangulo(int xi,int yi,int xf,int yf,int red,int green,int blue) { ... }
- Depois: Métodos com poucos argumentos
public void DesenhaRetangulo(Ponto cantoInicial,Ponto CantoFinal,RGB cor) { ... }
- Depois: refuzindo mais ainda os argumentos
public void DesenhaRetangulo(BordaRetangulo borda,RGB cor) { ... }
Funções não devem ter efeito colateral inesperado
public class Tarefa { private string _descricao; private DateTime _dataCriacao; public string Descricao { set { // Uma nova descrição é uma nova tarefa if (_descricao != value) { _dataCriacao = DateTime.Now; } _descricao = value; } get { return _descricao; } } }
Argumentos de saída
- Argumentos são naturalmente interpretado apenas como entrada
- Argumentos de saída devem ser evitados, se a função tiver que
mudar algo, então que mude o objeto que contém a mesma
As funções devem ser de comando ou de consulta mas nunca ambos logo um método deve retornar informações sobre um objeto ou mudar o seu estado mas ambas as tarefas frequentemente levam a confusão.
- Antes: AtribuirDado é consulta e comando
if (pessoa.AtribuirDado("Nome","Pedro")) { throw new ApplicationException("Atributo inválido"); }
- Depois: Existe um método para consulta e outro para comando
if (pessoa.PossuiAtributo("Nome")) { pessoa.AtribuirDados("Nome","Pedro"); }
Prefira exceções a retornar códigos de erro
Funções que retornam erros de código violam a regra de separação de comando e consulta pois estas fazem com que funções de comando sejam usados como funções de consulta. Finalmente, o maior problema é que o sistema que usa o método deve tratar o erro imediatamente no decorrer do fluxo.
- Antes: Metodos retornam código de erro
if (funcionario.CalcularSalario() == EVENTO_OK) { Salario salario = funcionario.ObterSalarioAtual(); if (!salario.TransferirValorParaContaBancaria() == EVENTO_OK) { throw new ErroEventoException("Erro ao transferir valor conta"); } if (!funcionario.Alegrar() == EVENTO_OK) { throw new ErroEventoExceptrion("Erro ao alegrar o funcionário"); } if (!funcionario.EnviarEmailAgradecendo() == EVENTO_OK) { throw new ErroEventoExceptrion("Erro ao enviar email agradecendo"); } }
- Depois: Métodos disparam exceções que são capturadas separadamente
try { funcionario.CalcularSalario(); Salario salario = funcionario.ObterSalarioAtual(); salario.TransferirValorParaContaBancaria(); funcionario.Alegrar(); funcionario.EnviarEmailAgradecendo(); } catch(Exception exception) { ... } finally { ... }
Não repita blocos de código (no copy-paste)
Código duplicado é a origem de vários males e por isso, vários padrões de projeto e práticas foram criados com o propósito de eliminar a duplicação. Entretanto, às vezes não é tão fácil identificar a repetição de código e essa repetição é uma omissão que abre a oportunidade para que erros aconteçam.