Ao lidar com algoritmos complicados em linguagens com suporte para funções aninhadas (como Python e D), geralmente escrevo funções enormes (porque o algoritmo é complicado), mas atenuo isso usando funções aninhadas para estruturar o código complicado. As funções enormes (mais de 100 linhas) ainda são consideradas más, mesmo que sejam bem estruturadas internamente através do uso de funções aninhadas?
Editar: para aqueles que não estão familiarizados com Python ou D, as funções aninhadas nessas linguagens também permitem o acesso ao escopo da função externa. Em D esse acesso permite a mutação de variáveis no escopo externo. No Python, ele permite apenas a leitura. Em D, você pode desabilitar explicitamente o acesso ao escopo externo em uma função aninhada declarando-o static
.
Respostas:
Lembre-se sempre da regra: uma função faz uma coisa e faz bem! Se você puder fazer isso, evite funções aninhadas.
Isso dificulta a legibilidade e o teste.
fonte
Alguns argumentaram que funções curtas podem ser mais propensas a erros do que funções longas .
Pessoalmente, descobri que o código linear bem comentado é mais fácil de seguir (especialmente quando você não o escreveu originalmente) do que quando é dividido em várias funções que nunca são usadas em outros lugares. Mas isso realmente depende da situação.
Eu acho que a principal vantagem é que, quando você divide um bloco de código, está trocando um tipo de complexidade por outro. Provavelmente existe um ponto ideal em algum lugar no meio.
fonte
Idealmente, toda a função deve estar visível sem precisar rolar. Às vezes, isso não é possível. Mas se você puder dividi-lo em pedaços, facilitará a leitura do código.
Sei que, assim que pressiono Page Up / Down ou movo para uma seção diferente do código, só consigo lembrar 7 +/- 2 coisas da página anterior. E, infelizmente, alguns desses locais serão usados ao ler o novo código.
Eu sempre gosto de pensar na minha memória de curto prazo como os registros de um computador (CISC, não RISC). Se você tiver a função inteira na mesma página, poderá ir ao cache para obter as informações necessárias de outra seção do programa. Se a função inteira não couber em uma página, isso seria o equivalente a sempre empurrar qualquer memória para o disco após cada operação.
fonte
Por que usar funções aninhadas, em vez de funções externas normais?
Mesmo que as funções externas sejam usadas apenas em sua única função anteriormente grande, ela ainda facilita a leitura de toda a bagunça:
fonte
Eu não tenho o livro na minha frente neste momento (para citar), mas, de acordo com o Code Complete, o "sweetspot" para o tamanho da função era de 25 a 50 linhas de código, de acordo com sua pesquisa.
Há momentos em que é aceitável ter funções longas:
Os horários em que não é bom ter funções longas:
O ponto principal é que a manutenção deve ser uma das maiores prioridades da sua lista. Se outro desenvolvedor não conseguir ver o seu código e obter uma "essência" do que o código está fazendo em menos de 5 segundos, o código não fornecerá "metadados" suficientes para dizer o que está fazendo. Outros desenvolvedores devem saber o que sua classe está fazendo apenas olhando para o navegador de objetos no IDE escolhido em vez de ler mais de 100 linhas de código.
Funções menores têm as seguintes vantagens:
A lista continua.....
fonte
A resposta é que depende, no entanto, você provavelmente deve transformar isso em classe.
fonte
Não gosto da maioria das funções aninhadas. Os lambdas se enquadram nessa categoria, mas geralmente não me sinalizam, a menos que tenham mais de 30 a 40 caracteres.
A razão básica é que ela se torna uma função altamente densa localmente com recursão semântica interna, o que significa que é difícil para mim envolver meu cérebro, e é apenas mais fácil enviar algumas coisas para uma função auxiliar que não sobrecarrega o espaço de código .
Considero que uma função deve fazer sua coisa. Fazer outras coisas é o que outras funções fazem. Portanto, se você tem uma função de 200 linhas, fazendo tudo, e tudo flui, tudo bem.
fonte
Isso é aceitável? Essa é realmente uma pergunta que somente você pode responder. A função alcança o que precisa? É sustentável? É 'aceitável' para os outros membros da sua equipe? Se sim, então é isso que realmente importa.
Editar: não vi nada sobre as funções aninhadas. Pessoalmente, eu não os usaria. Eu usaria funções regulares.
fonte
Uma função longa "linha reta" pode ser uma maneira clara de especificar uma longa sequência de etapas que sempre ocorrem em uma sequência específica.
No entanto, como outros mencionaram, essa forma é propensa a ter extensões locais de complexidade nas quais o fluxo geral é menos evidente. A colocação dessa complexidade local em uma função aninhada (ou seja, definida em outro local na função longa, talvez na parte superior ou inferior) pode restaurar a clareza do fluxo da linha principal.
Uma segunda consideração importante é controlar o escopo das variáveis que devem ser usadas apenas em um trecho local de uma função longa. A vigilância é necessária para evitar que uma variável introduzida em uma seção do código seja involuntariamente referenciada em outro lugar (por exemplo, após ciclos de edição), pois esse tipo de erro não será exibido como um erro de compilação ou tempo de execução.
Em alguns idiomas, esse problema é facilmente evitado: um trecho de código local pode ser agrupado em seu próprio bloco, como com "{...}", dentro do qual qualquer variável recém-introduzida é visível apenas para esse bloco. Algumas linguagens, como Python, não possuem esse recurso; nesse caso, funções locais podem ser úteis para impor regiões de escopo menores.
fonte
Não, as funções de várias páginas não são desejáveis e não devem passar na revisão de código. Também costumava escrever funções longas, mas depois de ler a refatoração de Martin Fowler , parei. Funções longas são difíceis de escrever corretamente, difíceis de entender e difíceis de testar. Eu nunca vi uma função de até 50 linhas que não seriam mais facilmente compreendidas e testadas se fossem divididas em um conjunto de funções menores. Em uma função de várias páginas, há quase certamente classes inteiras que devem ser fatoradas. É difícil ser mais específico. Talvez você deva postar uma de suas longas funções no Code Review e alguém (talvez eu) possa mostrar como melhorá-lo.
fonte
Quando estou programando em python, gosto de voltar atrás depois de escrever uma função e me perguntar se ela adere ao "Zen of Python" (digite 'import this' no seu interpretador python):
Bonito é melhor que feio.
Explícito é melhor que implícito.
Simples é melhor que complexo.
Complexo é melhor que complicado.
Flat é melhor que aninhado.
Esparso é melhor que denso.
Legibilidade conta.
Casos especiais não são especiais o suficiente para violar as regras.
Embora praticidade supere a pureza.
Os erros nunca devem passar silenciosamente.
A menos que seja explicitamente silenciado.
Diante da ambiguidade, recuse a tentação de adivinhar.
Deve haver uma - e preferencialmente apenas uma - maneira óbvia de fazê-lo.
Embora esse caminho possa não ser óbvio a princípio, a menos que você seja holandês.
Agora é melhor do que nunca.
Embora nunca seja melhor do que certoagora.
Se a implementação é difícil de explicar, é uma má ideia.
Se a implementação é fácil de explicar, pode ser uma boa ideia.
Os espaços para nome são uma ótima idéia - vamos fazer mais!
fonte
Coloque-os em um módulo separado.
Supondo que sua solução não seja mais inchada do que precisar, você não tem muitas opções. Você já dividiu as funções em diferentes sub-funções, então a questão é onde você deve colocá-lo:
Agora, a segunda e a terceira alternativas estão, de certa forma, aninhadas, mas a segunda alternativa, por alguns programadores, não parece ruim. Se você não excluir a segunda alternativa, não vejo muitas razões para excluir a terceira.
fonte