60
Código Limpo Capítulo 3 - Funções Bruno Blumenschein Hélios Kárum de Oliveira Bastos Rodrigo Oliveira Andrade

Código limpo: Funções Capítulo 3

Embed Size (px)

Citation preview

Page 1: Código limpo: Funções  Capítulo 3

Código LimpoCapítulo 3 - Funções

Bruno BlumenscheinHélios Kárum de Oliveira BastosRodrigo Oliveira Andrade

Page 2: Código limpo: Funções  Capítulo 3

Regras

● Primeira Regra:○ As funções devem ser pequenas;

● Segunda Regra:○ "Elas precisam ser ainda menores";

● Não há como provar porque, foi baseado nas tentativas e erros do autor.

● Não ultrapassar mais do que 20 linhas, com 150 carcteres por linha.

Page 3: Código limpo: Funções  Capítulo 3

Exemplo

Page 4: Código limpo: Funções  Capítulo 3

Exemplo - Continuação

Page 5: Código limpo: Funções  Capítulo 3

Exemplo - Primeira Melhoria

Page 6: Código limpo: Funções  Capítulo 3

Exemplo - Segunda Melhoria

Page 7: Código limpo: Funções  Capítulo 3

Blocos e Identação

● Seguindo a linha de se minimizar funções, instruções como if, else e while devem possuir apenas uma linha de código.

● E provavelmente esta linha será uma chamada a uma função.

● Além de manter a função pequena agrega valor de documentação, já que o nome da função deve ser bem descritivo.

Page 8: Código limpo: Funções  Capítulo 3

Blocos e Identação

● Essa estrutura também implica que as funções não devem ter muitas estruturas aninhadas.

● Para facilitar a estrutura e identação o nível máximo de estruturas aninhadas deve ser de uma ou duas.

Page 9: Código limpo: Funções  Capítulo 3

Fazer apenas uma Coisa

● "Functions should do one thing. They should do it well. The should do it only."

● Funções devem fazer uma única coisa. Elas devem fazê-la bem. Elas devem fazer apenas ela.

● O problema é saber o que é "uma única coisa".

Page 10: Código limpo: Funções  Capítulo 3

Fazer apenas uma Coisa

● O que faz o programa do exemplo anterior (RenderPageWithSetupsAndTeardowns):1. Determina se a página é de teste;2. Se for, inclui setUps e tearDowns;3. Renderiza a página em HTML.

● É apenas uma função ou são três?● Note que as três operações realizadas estão

a um nível abaixo do nome da função.● Então ela está fazendo uma coisa só.

Page 11: Código limpo: Funções  Capítulo 3

Fazer apenas uma Coisa

● O motivo de criarmos uma função é para decompor um conceito maior (em outras palavras o nome da função) em uma série de passos no próximo nível de abstração.

Page 12: Código limpo: Funções  Capítulo 3

Seções em Funções

● Funções que fazem apenas apenas uma coisa não podem ser razoavelmente divididas em seções.

● Caso isto aconteça é um sintoma de estar fazendo mais de uma coisa.

Page 13: Código limpo: Funções  Capítulo 3

Um Nível de Abstração por Função

● A fim de confirmar se uma função só faz uma coisa, precisamos verificar se todas as intruções dentro da função estão no mesmo nível de abstração.

● Vários níveis de abstração dentro de uma função sempre geram confusão.

● Os leitores podem não conseguir dizer se uma expressão determinada é um conceito essêncial ou um mero detalhe.

Page 14: Código limpo: Funções  Capítulo 3

Ler o Código de Cima para Baixo

● O código deve ser lido de maneira top-down.

● Desejamos que cada função seja seguida pelas outras no próximo nível de abstração, de modo que possamos ler o programa um nível de cada vez.

● Chamamos isso de regra descendente.

Page 15: Código limpo: Funções  Capítulo 3

Exemplo

● Para incluir setups e teardowns, nós incluimos primeiro os setups, depois nós incluimos o conteúdo da página de testes, e então incluimos os teardowns;

● Para incluir os setups, nós incluimos o suite setup se for uma suite, e então incluimos o setup regular;

● Para incluir o suite setup, nós procuramos na hierarquia pela página "SuiteSetUp", e então adicionamos uma instrução com o caminho para aquela página.

● Para procurar a hierarquia a cima...

Page 16: Código limpo: Funções  Capítulo 3

Ler o Código de Cima para Baixo

● Acaba sendo difícil para o programador aplicar essa regra, mas quando ele passa a dominar esse truque, ele passa a ter o controle de verificação para saber se uma função só faz apenas uma coisa.

Page 17: Código limpo: Funções  Capítulo 3

Switch

● É difícil criarmos um swtich pequeno.● Mesmo o menor switch possível, com dois

casos, já é maior do que eu gostaria de ter em um único bloco de código.

● É difícil criar um switch que faça apenas uma coisa.

● Pela sua natureza eles são criados para fazerem N coisas.

● Infelizemente nós não podemos evitar o uso de switchs.

Page 18: Código limpo: Funções  Capítulo 3

Switch

● Mas nós podemos nos assegurar que cada instrução está um nível de abstração abaixo e que nunca se repetem.

● Switch geralmente tem vários problemas com o Principio do Aberto-Fechado. Já que para cada alteração, deve-se abrir a função e alterar o switch.

Page 19: Código limpo: Funções  Capítulo 3

Use Nomes Descritivos

● Devemos criar nomes que descrevem bem o que as funções fazem.

● Princípo de Ward: "Você sabe que está criando um código limpo quando cada rotina que você lê é como você esperava."

● Metade do esforço para satisfazer essa máxima é escolher bons nomes para as funções que fazem apenas uma coisa.

● Quanto menor e mais centralizada for a função mais fácil será para se pensar em um nome descritivo.

Page 20: Código limpo: Funções  Capítulo 3

Use Nomes Descritivos

● Nomes extensos são maiores do que nomes pequenos e enigmáticos.

● Um nome extenso e descritivo é melhor que um comentário extenso e descritivo.

● Use uma convenção de nomenaclatura que possibilite uma fácil leitura das funções com vários nomes.

● Não se preocupe com o tempo para criar um nome, e não tenho medo de modificá-lo caso tenha encontrado uma opção melhor.

Page 21: Código limpo: Funções  Capítulo 3

Use Nomes Descritivos

● É comum que ao se buscar nomes adequados resulte em uma boa reestruturação do código.

● Seja consistente nos nomes, utilize sempre as mesmas frases, substantivos e verbos.

Page 22: Código limpo: Funções  Capítulo 3

Parâmetros de Função

● O número ideal de argumentos para um método é zero.

● Depois, um argumento, mônade, e em seguida, dois argumentos, díade.

● Métodos com três argumentos, tríade, ou mais, devem ser evitados sempre que possível. Necessitam de uma boa justificativa caso sejam utilizados.

Page 23: Código limpo: Funções  Capítulo 3

Parâmetros de Função

● Parâmetros são complicados. Eles requerem bastante conceito. Nos exemplos eles foram até retirados.

● É mais fácil enteder o método: includeSetupPage() do que o método: includeSetupPageInto(newPage-Content).

Page 24: Código limpo: Funções  Capítulo 3

Parâmetros de Função

● Os parâmetros são ainda mais problemáticos do ponto de vista de testes.

● Imagine criar todos os cenários de testes posíveis para todas as combinações existentes entre os parâmetros.

● Se não houver nenhum parâmetro, esta é uma tarefa simples, se já existir um, não é tão difícil assim.

● Com dois a situação já passa a ser desafiadora.

Page 25: Código limpo: Funções  Capítulo 3

Parâmetros da Função

● Os parâmetros de saída são ainda mais difíces de entender do que os de entrada.

● Geralmente não esperamos dados saídos por parâmetros.

● Um parâmetro de entrada é a melhor coisa depois do zero parâmetro.

●É facil entender:○ SetupTeardown-Includer.render(pageData)○ E fica bem claro que renderizemos os dados em

pageData.

Page 26: Código limpo: Funções  Capítulo 3

Formas Mônades Comuns

● Há duas razões comuns para se passar um único parâmetro para uma função:○ Você pode estar fazendo uma pergunta sobre

aquele parâmetro:■ boolean fileExists(“MyFile”).

○ Você pode estar trabalhando aquele parâmetro, transformando-o em outra coisa e retornando-o■ InputStream fileOpen(“MyFile”)■ Transforma a String do nome de um arquivo em

um valor retornado pela InputStream.

Page 27: Código limpo: Funções  Capítulo 3

Formas Mônades Comuns

● Outra forma menos comum é para a utilização de eventos:○ Há um parâmetro de entrada, mas não há um de

saida.○ O programa em si serve para interpretar a chamada

da função como um evento, e usar o parâmetro para alterar o estado do sistema.

○ void passwordAttemptFailedNtimes(int attempts).

Page 28: Código limpo: Funções  Capítulo 3

Formas Mônades Comuns

● Tente evitar funções que não sigam estas formas, como: ○ void includeSetupPageInto(StringBuffer pageText)

● Usar um parâmetro de saída em vez de um valor de retorno para uma modificação fica confuso.

● Se uma função vai transformar o seu parâmetro de entrada, a alteração deve aparecer como o valor retornado.

Page 29: Código limpo: Funções  Capítulo 3

Parâmetros Lógicos

● "Parâmetros lógicos são feios".● Utilizar um valor booleano como parâmetro

em uma função é certamente uma prática horrível, pois ele complica imediatamente a assinatura do método, mostrando explicitamente que o método faz mais de uma coisa.

Page 30: Código limpo: Funções  Capítulo 3

Exemplo

● Um método: render(true) é difícil de ser interpretado por um leitor simples.

● Ele fica melhor: render(boolean isSuite), mas nem tanto.

● O melhor seria dividí-lo em:○ renderForSuite() e○ renderForSingleTest().

Page 31: Código limpo: Funções  Capítulo 3

Funções Díades

● Uma função com dois parâmetros é mais fácil de enteder do que uma com apenas um. A função writeField(name) é mais fácil de compreender do que a writeField(output-Stream, name).

● Embora as duas estejam claras, a primeira apresenta seu propósito explicitamente quando lemos.

● Já a segunda requer uma pequena pausa até que aprendemos a ignorar o primeiro parâmetro.

Page 32: Código limpo: Funções  Capítulo 3

Funções Díades

● E é ai que mora o problema, porque é nas partes do código que ignoramos que mora o problema.

● Díades não são ruins, e certamente terão de ser utilizá-dos. Entretanto deve-se estar ciente de que haverá um preço a pagar e, portanto, deve-se em tirar proveito dos mecanismos disponíveis a você para convertê-los em mônades.

Page 33: Código limpo: Funções  Capítulo 3

Tríades

● Funções que recebem três parâmetros são consideravelmente mais difíceis de entender do que as que utiliza ois parâmetros.

● A questão de ordenação, pausa, ignoração apresentam mais do que o dobro de dificuldade.

● Portanto as utilize somente quando houver extrema necessidade.

Page 34: Código limpo: Funções  Capítulo 3

Tríades - Exemplo

● assertEquals(message, expected, actual).● O parâmetro message precisa ser deduzido,

e as vezes não é entendido pelo programados qual é a sua função.

Page 35: Código limpo: Funções  Capítulo 3

Tríades - Exemplo 2

● Um método como:○ Circle makeCircle(double x, double y, double radius)

● Ser transformado para:○ Circle makeCircle(Point center, double radius);

● Pode parecer uma trapaça, mas x e y são partes de um conceito maior e merecer ter uma denominação diferenciada.

Page 36: Código limpo: Funções  Capítulo 3

Nomenclatura

● Como dito anteriormente, é importante que as funções tenham nomes que expliquem diretamente o que elas fazem.

● Mas tão importante quanto isso, é que estes nomes também relacionem como o parâmetro vai interagir com a função.

Page 37: Código limpo: Funções  Capítulo 3

Nomenclatura - Exemplos

● Em caso de de mônades a função e o parâmetro devem ser criados baseados em um bom par de verbo/substantivo.○ write(name)

● O que quer que seja o parâmetro "name", sebemos que é ele que será "escrito" (write).

● Um nome melhor ainda seria:○ writeField(name)

● Que nos dias ainda que o nome é um campo (field).

Page 38: Código limpo: Funções  Capítulo 3

Evite Efeitos Colaterais

● "Efeitos colaterais são mentiras".● Você promete fazer uma coisa com a sua

função, mas ela faz outras coisas escondidas, isto é errado.

● Veremos no exemplo a seguir o que seria um exemplo de efeito colateral em um sistema que está fazendo login.

Page 39: Código limpo: Funções  Capítulo 3

Evite Efeitos Colaterais - Exemplo

Page 40: Código limpo: Funções  Capítulo 3

Evite Efeitos Colaterais

● O efeito colateral deste código está na linha:○ Session.initialize();

● Pelo nome da função (checkPassword), ela verifica a senha, mas não é dito que ela inicia a sessão.

● Então alguém que acredita no que diz o nome da função, correrá o risco de apagar todos os dados da sessão existente caso ele deseje autenticar o usuário.

Page 41: Código limpo: Funções  Capítulo 3

Evite Efeitos Colaterais

● Este efeito colateral cria um acoplamento temporário.

● Esta função só poderá ser chamada em casos específicos (quando for seguro inicializar a sessão).

● Se for chamada fora de ordem sem querer, os dados serão perdidos.

● Um nome melhor para o método poderia se:○ checkPasswordAndInitializeSession

Page 42: Código limpo: Funções  Capítulo 3

Parâmetros de Saída

● Parâmetros são comumente interpretados como entradas de uma função.

● Quando encontramos parâmetros de saída em alguma função, temos que gastar um tempo bem maior para ler e relê-la.

● Como por exemplo:○ appendFooter(s);

● Esta função anexa s como um rodapé (footer) em algo? Ou anexa um rodapé a s?

● s é uma entrada ou uma saída?

Page 43: Código limpo: Funções  Capítulo 3

Parâmetros de Saída

● Analisando a assinatura da função:○ public void appendFooter(StringBuffer report);

● A questão é esclarecida, mas a custa da verificação da declaração da função.

● Isto é considerado uma relida, uma iterrupção de raciocínio e deve ser evitado.

● De modo geral deve-se evitar parâmetros de saída. Uma utilização melhor do método seria:○ report.appendFooter();

Page 44: Código limpo: Funções  Capítulo 3

Parâmetros de Saída

● Caso a função precise alterar o estado de algo, faça-a mudar o estado do objeto a que pertence.

Page 45: Código limpo: Funções  Capítulo 3

Separação Comando-Consulta

● Funções devem fazer algo ou responder a algo. Mas nunca as duas coisas.

● Sua função ou altera o estado de um objeto ou retorna informações sobre ele.

● Efetuar as duas tarefas gera confusão.

Page 46: Código limpo: Funções  Capítulo 3

Separação Comando-Consulta Exemplo

● Analisando o método:○ public boolean set(String attribute, String value);

● Esta função define o valor de um dado atributo e retorna verdadeiro se obtiver êxito e falso se tal atributo não existir.

● Isto leva a instruções estranhas que podem ser difíceis de serem interpretadas como:○ if (set("username", "unclebob"))...

Page 47: Código limpo: Funções  Capítulo 3

Separação Comando-Consulta Exemplo

● Do ponto de vista de um leitor:○ Está perguntando se o atributo "username"

anteriormente recebeu o valor "unclebob"?○ Ou se "username" obtêve êxito ao receber o valor

"ubclebob"?● Fica difícil adivinhar.

Page 48: Código limpo: Funções  Capítulo 3

Prefira Exceções a Retorno de Código de Erro

● Fazer funções retornarem códigos de erros é uma leve violação da separação comando-consulta.

● Estruturas deste tipo podem gerar trechos de códigos aninhados, fazendo com que o erro deve ser tratado imediatamente.

● Com a utilização de exções podemos tratar os erros em separado.

Page 49: Código limpo: Funções  Capítulo 3

Exemplo Ruim

Page 50: Código limpo: Funções  Capítulo 3

Exemplo Bom

Page 51: Código limpo: Funções  Capítulo 3

Extraia os Blocos de Try/Catch

● Blocos de código de Try/Catch são particularmente "feios" a seu modo.

● Por isso, é melhor extrair os corpos de try e catch em funções fora da sua própria estrutura.

● Como no exemplo a seguir:

Page 52: Código limpo: Funções  Capítulo 3

Extraia os Blocos de Try/Catch Exemplo

Page 53: Código limpo: Funções  Capítulo 3

Tratamento de Erro é uma Coisa Só

● Funções devem fazer apenas uma coisa. ● Tratamento de erro é uma coisa.● Criar classes de erro em java, como a

seguir, não é interessante.

Page 54: Código limpo: Funções  Capítulo 3

Tratamento de Erro é uma Coisa Só

● Classes como estas são "chamarizes a dependência".

● Muitas outras classes devem importá-las e usá-las.

● O que coloca uma pressão na classe Error.● Os programadores não querem colocar

novos erros para não ter que recompilar.● A melhor maneira de evitar isto é utilizando

exceções.

Page 55: Código limpo: Funções  Capítulo 3

Evite Reptição

● As vezes não é fácil identificar repetição de código,consequentemente arrumar erros em repetições são difíceis.

● Repetições causam trabalhos dobrados caso o código precisa ser modificados.

● Repetição de código é o mal de toda progamação.

Page 56: Código limpo: Funções  Capítulo 3

Exemplo Reptição

Page 57: Código limpo: Funções  Capítulo 3

Exemplo Repetição

Page 58: Código limpo: Funções  Capítulo 3

Programação Estruturada

● Conceito de Edsger Dijkstra.● Cada função deve ter apenas uma entrada e

uma saída.● Não há muita vantagem em seguir essa

estrutura em funções pequenas.● Se você conseguir manter funções

pequenas, instruções como return, break e continue podem sem vantajosas.

● GoTo nunca deve ser utilizado.

Page 59: Código limpo: Funções  Capítulo 3

Como escrever Funções como Esta?

● Não é um processo rápido, envolvem vários passos.

● Primeiro pense em como resolver o problema e faça da maneira que desejar.

● Só depois vá refinando as funções e aplicando as regras explicadas no capítulo para que fiquem melhores estruturadas.

● Não hesite em decompor toda uma classe se sentir necessário, valerá a pena no futuro.

Page 60: Código limpo: Funções  Capítulo 3

Conclusão

● Funções são os verbos do sistema.● As Funções são essenciais para o

entendimento do sistema.● As funções precisam ajudar o

funcionamento do sistema.● Funções precisam ser curtas, bem

nomeadas, e bem organizadas.