O que é comprovado como um bom comprimento máximo de uma função? [fechadas]

44

O comprimento da função afeta a produtividade de um programador? Em caso afirmativo, qual é um bom número máximo de linhas para evitar perda de produtividade?

Como esse é um tópico altamente opinativo, faça backup da reivindicação com alguns dados.

Peter Mortensen
fonte
6
O comprimento não deve ser medido em LOC, mas na quantidade de tempo necessária para compreender exatamente o que ele faz. E essa duração não deve demorar mais de um minuto. Se não consigo descobrir em alguns segundos, provavelmente está fazendo muito ... depois de um minuto, definitivamente está.
CaffGeek
13
O comprimento máximo deve ser 17.
ThomasX
11
Pense S no SOLID.
Kris Krause
11
@ CaffGeek Ou talvez a função esteja simplesmente fazendo algo não trivial. Vi funções que levariam dias para entender completamente. Até funções em que eu entendo todos os conceitos envolvidos podem facilmente levar meia hora para trabalhar com os detalhes. Embora seja bom ter funções triviais, muitos problemas são simplesmente intrinsecamente difíceis.
CodesInChaos

Respostas:

46

Desde que embarquei nessa raquete louca em 1970, vi exatamente um módulo que realmente precisava ser mais do que uma página impressa (cerca de 60 linhas). Eu já vi muitos módulos que eram mais longos.

Na verdade, escrevi módulos que eram mais longos, mas geralmente eram grandes máquinas de estado finito, escritas como grandes instruções de chave.

Parte do problema parece ser que os programadores hoje em dia não são ensinados a modularizar as coisas.

Os padrões de codificação que maximizam o desperdício de espaço vertical também parecem fazer parte do problema. (Ainda não conheci um gerente de software que leu " Psicologia da programação de computadores ", de Gerald Weinberg . Weinberg ressalta que vários estudos mostraram que a compreensão do programador é essencialmente limitada ao que o programador pode ver a qualquer momento. programador precisa rolar ou virar uma página, sua compreensão diminui significativamente: eles precisam se lembrar e abstrair.)

Continuo convencido de que muitos dos ganhos de produtividade de programadores bem documentados da FORTH foram devidos ao sistema "bloco" FORTH para código fonte: os módulos eram limitados a um máximo absoluto de 16 linhas de 64 caracteres. Você poderia fatorar infinitamente, mas em nenhuma circunstância poderia escrever uma rotina de 17 linhas.

John R. Strohm
fonte
3
Toda a filosofia da FORTH foi projetada para incentivar isso ... Você pretendia criar seu próprio vocabulário, dividindo incansavelmente seu programa em partes cada vez menores, terminando com menos um script e mais um dicionário. Limites de comprimento por si só não fazem isso - você verá uma rotina dividida em partes arbitrárias apenas para satisfazer algum padrão de codificação. Eu acho que você está absolutamente correto ao suspeitar que os programadores simplesmente "não são ensinados a modularizar as coisas"; essa deveria ter sido uma das grandes vitórias do OOP, mas por várias razões, muitas vezes é enfatizada como uma meta em si mesma.
Shog9
11
@Sr. CRT: Os limites de comprimento nas implementações FORTH de implementações orientadas a bloco. A natureza interativa da maioria dos FORTHs ajuda, incentivando pequenos módulos e testes rápidos desses módulos. O D85, um FORTH baseado em arquivo, não forçou a modularização, e vi caras brincando com o D85 escreverem muitos módulos de consciência do fluxo de programador para sempre. Daí minha convicção. (. Por que vale a pena, discorda Liz sim com me Ela acha que é principalmente a interatividade que dá à luz o aumento de produtividade.)
John R. Strohm
2
+1 me apresentar o grande livro "Psychology of Computer Programming" :)
Samuel
30

Qual é o tamanho certo, realmente?

Depende do idioma que você usa, mas em geral (e para o meu gosto pessoal):

  • Idealmente , menos de 25 linhas.
  • Aceitavelmente , menos de 35 linhas.

Se for mais, é algo que eu preciso voltar mais tarde e refazer.

Mas , realisticamente , qualquer tamanho que precise ser quando você precisa entregar algo e que faz mais sentido no momento cuspi-los assim, às vezes fica ainda mais fácil para alguém revisar antes do envio. (mas ainda assim voltaremos mais tarde).

(Recentemente, minha equipe executou um programa em nossa base de código: encontramos classe com 197 métodos e outro com apenas 3 métodos, mas um deles tinha 600 linhas. Jogo bonito: qual é o pior dos 2 males?)


Agora, para uma resposta mais zen ... Em geral, é considerado uma boa prática (TM) citar um ou dois grandes homens, então aqui vai:

Tudo deve ser feito o mais simples possível, mas não mais simples. - A. Einstein

Finalmente, a perfeição é alcançada não quando não há mais nada a acrescentar, mas quando não há mais nada a ser levado. - A. de Saint Exupéry


Adendo sobre estilos de comentários

Como um adendo a isso, suas funções devem ter nomes claros explicando suas intenções. Em relação aos comentários, geralmente não comento dentro de uma função:

  • os comentários dizem "por quê?" ,
  • o código diz "como?" .

Um bloco de comentários na parte superior de cada função (que requer explicação) é suficiente. Se sua função é pequena e os nomes das funções são explícitos o suficiente, você deve apenas dizer o que deseja alcançar e por quê. Uso comentários embutidos apenas para campos em alguns idiomas ou no início de blocos para funções que quebram as regras de 25 a 35 linhas se a intenção não for clara. Eu uso um comentário de bloco dentro do código quando ocorrem situações excepcionais (um bloco de captura em que você não precisa ou deseja fazer nada deve ter um comentário dizendo o porquê, por exemplo).

Para mais informações, leia minha resposta sobre Estilo e recomendações de código de comentários

haylem
fonte
@haylem Eu acho que esta é a versão do programador de Freddy vs. Jason :-)
Gaurav
Concordo, no entanto, gostaria de acrescentar que deve ser do tamanho de uma página de tela. se você tiver 72 linhas em uma página da tela, a função não deverá exceder 72 linhas.
Nome de exibição
@ É necessário garantir que uma única função seja mantida em uma página da tela, para não rolar, mas essa não é minha principal preocupação. Minha preocupação é a quantidade de tempo que leva para processar informações ao ler uma função, e é quase instantâneo se for claramente escrito em 25 linhas. Torna-se apenas uma questão de seguir as chamadas de função. 72 é muito grande para mim (mais, e se você tem telas divididas E isso seria dependente do tipo de letra Mas eu concordo para o valor histórico da recomendação?.)
haylem
11
Obviamente, às vezes você tem funções nas quais copia 70 campos diferentes para 70 lugares diferentes. Eu geralmente uso ttpara gerá-los, mas às vezes você fica preso a uma função de bunda longa (ou uma função de bunda longa) que realmente não faz nada de interessante, então não é um problema real.
configurator
11
Quando a função é, por exemplo Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);, fica claro que é tudo a mesma coisa. (Observe que este é apenas um exemplo; esse tipo de função aparece de tempos em tempos)
configurator
12

Na minha opinião, toda função deve ser a menor possível. Cada função deve fazer apenas uma coisa e fazê-lo bem. Isso realmente não responde à questão do tamanho máximo, mas são meus sentimentos quanto ao tamanho das funções.

Para usar as palavras do tio Bob, "Extraia até que você não possa mais extrair. Extraia até cair".

Jason
fonte
2
O que você quer dizer com o menor possível? Ser tão pequeno quanto possível seria ter cada função apenas com duas linhas: uma para executar uma única operação e outra que chama uma função para fazer o resto?
Kelmikra
Tio Bob novamente. Pense por você mesmo. Não ouça tios. Eles o desviam.
gnasher729 10/11
10

Qual deve ser a altura máxima de um edifício? Depende de onde a construção está ou da altura que você deseja que ela seja.
Você pode obter respostas diferentes de pessoas diferentes que vêm de cidades diferentes.
Algumas funções de script e manipuladores de interrupção do kernel são muito longos.

Huang F. Lei
fonte
Estou totalmente de acordo com você. Eu gosto de metáforas. :) Um prédio de três andares pode ter sido construído por um arquiteto idiota que não sabe onde colocar uma saída de segurança válida e outro prédio pode ter dez andares e ser um projeto arquitetônico perfeito. Devemos sempre ter em mente que a legibilidade e a manutenção devem ser o principal motivo para refatorar um método para reduzir seu tamanho e não o tamanho em si. Uma cidade não poderia ser construída com 90% dos arranha-céus, exceto nos filmes de ficção científica. :)
Samuel
10

Um método que funciona para mim é: Posso fazer com que uma parte de uma função mais longa dê um nome que faça sentido. Eu acho que a duração de um método não é tão importante quanto a boa nomeação. O método deve fazer o que o nome diz, nem mais nem menos. E você deve ser capaz de dar um bom nome. Se você não pode nomear seu método como bom, provavelmente o código não é bom.

Mnementh
fonte
E seu método deve fazer apenas uma coisa para ter um bom nome ... não 'And', 'Or' ou qualquer outra coisa que faça o método nomear 50 caracteres.
Samuel
10

Contanto que ele precise fazer o que precisa, mas não mais.

Paul Nathan
fonte
como dizem em c, "Não há nenhum problema em c que você não possa resolver adicionando outro ponteiro a esse ponteiro". você sempre pode adicionar outra função abaixo dela, a pergunta dele seria a sua epifania "onde termina?"
Nome de exibição
11
Eu realmente lançá-lo e dizer "tão curto quanto ele precisa ser, mas não menor", mas você me +1 para estar perto o suficiente :)
Ben Hughes
6

Eu acho que há uma troca. Se você possui muitos métodos curtos, geralmente é mais difícil depurá-los do que um método longo. Se você precisar pular o editor 20 ou 30 vezes para rastrear uma chamada de método, será difícil manter tudo em sua cabeça. Enquanto isso, se houver um método claro e bem escrito, mesmo que seja 100 linhas, geralmente é mais fácil ficar na sua cabeça.

A verdadeira questão é por que os itens devem estar em métodos diferentes, e a resposta, como dada acima, é a reutilização do código. Se você não está reutilizando o código (ou não sabe), pode fazer sentido deixá-lo em um método gigante fácil de seguir e, quando precisar reutilizá-lo, divida as partes que precisam ser reutilizadas. usando em métodos menores.

Na realidade, parte do bom design de métodos é criar métodos funcionalmente coesos (essencialmente eles fazem uma coisa). O comprimento dos métodos não importa. Se uma função faz uma coisa bem definida e tem 1.000 linhas, é um bom método. Se uma função faz 3 ou 4 coisas e tem apenas 15 linhas, é um método ruim ...

Cervo
fonte
Eu gosto de métodos curtos.
Marc
Gosto do que você disse, porque dizer que um método não deve ter mais de 10 linhas é uma utopia. Ok, é uma boa regra ter em mente cada vez que você escreve um método, mas não deve ser uma regra matemática como 1 + 1 = 2. Se você respeitar princípios como KISS, DRY, YAGNI, etc ... e seus métodos forem sem comentários completos, explicando alguns detalhes porque são muito longos, os métodos podem ter 100 linhas de código e podem ser totalmente limpos para entender e manter. No entanto, deve ser mais uma exceção do que um hábito. Eu acho que mudar de caso no método de fábrica é um bom exemplo de exceção.
Samuel
5

Acho mais fácil acompanhar o que estou fazendo, se consigo ver toda a função de uma só vez. Então, aqui está como eu prefiro escrever funções:

  1. Curto o suficiente para caber no meu monitor com uma fonte razoável.
  2. Se precisar ser maior que o nº 1, curta o suficiente para imprimir em um pedaço de papel em uma fonte razoável.
  3. Se precisar ser maior que o nº 2, curta o suficiente para imprimir em 2 páginas em um pedaço de papel.

Eu raramente escrevo funções por mais tempo que isso. A maioria delas são instruções de comutação C / C ++ gigantes.

Bob Murphy
fonte
Curto o suficiente para caber em um monitor é bom, mas especificar o tipo e tamanho da fonte. O papel não deve ser uma regra, na minha opinião, porque estamos em 2013 e quem ainda imprime o código no papel, quem o imprime apenas para ver se cabe em um tamanho de papel? Com ferramentas como o Visual Studio, intellisense, não há mais motivo para analisar o código com papel.
Samuel
5

Para mim, uma função tem o tamanho que precisa ser. Na maioria das vezes que eu o divido, reutilizarei o código.

Basicamente, vou me ater à principal 'alta coesão, baixo acoplamento' e não há restrição de comprimento.

Ross
fonte
5

A questão deve ser quantas coisas uma função deve fazer. E, geralmente, é raro você precisar de 100 linhas para fazer "uma" coisa. Novamente, isso depende do nível em que você está visualizando o código: hashing de uma senha é uma coisa? Ou fazer hash e salvar a senha é uma coisa?

Eu diria, comece salvando a senha como uma função. Quando você sente que o hash é diferente e refatora o código. Eu não sou um programador especialista, mas IMHO, toda a idéia de funções começa pequena é que, quanto mais atômicas forem suas funções, maior a chance de reutilização do código, sem precisar fazer a mesma alteração em mais de um local , etc.

Eu vi procedimentos armazenados SQL que executam mais de 1000 linhas. O número de linhas de procedimentos armazenados também é menor que 50? Eu não sei, mas isso torna a leitura do código um inferno. Não apenas é necessário continuar rolando para cima e para baixo, você precisa dar um nome a algumas linhas de código como "this validation1", "this updates in the database" etc. etc. - um trabalho que o programador deveria ter feito.

Narayana
fonte
+1 apenas para o primeiro parágrafo. tudo o resto é relativo ao caso em que você está trabalhando.
Nome de exibição
E como a divisão em 50 funções ajuda? Quando as funções precisam se comunicar, para que você não termine com 500 linhas, mas mil?
gnasher729 10/11
5

Da complexidade ciclomática (Wikipedia):

A complexidade ciclomática de uma seção do código fonte é a contagem do número de caminhos linearmente independentes através do código fonte.

  • Eu recomendo que você mantenha esse número abaixo de 10 em um único método. Se chegar a 10, é hora de re-fatorar.

  • Existem ferramentas que podem avaliar seu código e fornecer um número de complexidade ciclomática.

  • Você deve se esforçar para integrar essas ferramentas ao seu pipeline de construção.

  • Não procure literalmente um tamanho de método, mas tente analisar sua complexidade e responsabilidades. Se houver mais de uma responsabilidade, provavelmente será uma boa idéia re-fatorar. Se sua complexidade ciclomática aumentar, provavelmente é hora de se re-fatorar.

  • Estou bastante certo de que existem outras ferramentas que fornecem feedback semelhante, mas ainda não tive a chance de analisar isso.

CodeART
fonte
A complexidade ciclomática mostrou, no código real de um dos grandes repositórios públicos, não exemplos inventados, que está fortemente correlacionada com o SLOC bruto. Isso o torna basicamente inútil, pois é muito mais fácil contar os retornos de carro. (Sim, é possível SLOC jogo Seja honesto, aqui: Quanto tempo seria alguém que foi gaming as métricas SLOC no seu empregador ser autorizados a continuar a desenhar um salário.?)
John R. Strohm
4

Normalmente, tento manter meus métodos / funções no que cabe na tela de um monitor 1680x1050. Se não se encaixar, use métodos / funções auxiliares para dividir a tarefa.

Ajuda a legibilidade na tela e no papel.

Jason
fonte
Eu faço a mesma coisa, mas vale a pena especificar qual tipo e tamanho de fonte você está usando. Para mim, prefiro "consolas" com um tamanho de 14, conforme sugerido por Scott hanselman. hanselman.com/blog/… É difícil, pela primeira vez, trabalhar com uma fonte tão grande, mas é uma prática recomendada lembrar sempre de que seu método deve ser o menor possível.
Samuel
4

Não limitei nada a nada, porque algumas funções implementam algoritmos que são inerentemente complexos e qualquer tentativa de reduzi-los tornaria as interações entre as novas funções mais curtas tão complicadas que o resultado líquido não reduziria a simplicidade. Também não acredito que a ideia de que uma função deva fazer apenas "uma coisa" seja um bom guia, pois "uma coisa" em um alto nível de abstração pode ser "muitas coisas" em um nível inferior.

Para mim, uma função é definitivamente muito longa se seu comprimento causar violações sutis do DRY no momento, e extrair parte da função em uma nova função ou classe poderia resolver isso. Uma função pode ser muito longa se esse não for o caso, mas uma função ou classe pode ser facilmente extraída para tornar o código mais modular de uma maneira que provavelmente será útil em face de mudanças previsíveis no futuro.

dsimcha
fonte
As funções fazem "uma coisa" em um nível de abstração específico, e você só se preocupa com esse nível de abstração. Esse é o ponto. Se você não consegue entender isso, acho que você não entende abstração.
Zoran Pavlovic
4

Curto o suficiente para ser otimizado corretamente

Os métodos devem ser tão curtos que façam exatamente uma coisa. A razão para isso é simples: para que seu código possa ser otimizado corretamente.

Em uma linguagem JIT-ted como Java ou C #, é importante que seus métodos sejam simples para que o compilador JIT possa produzir código rapidamente. Métodos mais longos e complicados naturalmente exigem mais tempo de JIT. Além disso, os compiladores JIT oferecem apenas algumas otimizações e apenas os métodos mais simples se beneficiam disso. Esse fato foi mencionado no Effective C # de Bill Wagner .

Em uma linguagem de nível inferior, como C ou C ++, ter métodos curtos (talvez uma dúzia de linhas) também é importante, pois dessa forma você minimiza a necessidade de armazenar variáveis ​​locais na RAM e não em um registro. (Aka 'Register Spilling'.) Observe, porém, que nesse caso não gerenciado, o custo relativo de cada chamada de função pode ser bastante alto.

E mesmo em uma linguagem dinâmica, como Ruby ou Python, ter métodos curtos também ajuda nas otimizações do compilador. Em uma linguagem dinâmica, quanto mais dinâmica é uma característica, mais difícil é otimizar. Por exemplo, um método longo que pega um X e pode retornar um Int, Float ou String provavelmente terá um desempenho muito mais lento que três métodos separados, cada um retornando apenas um único tipo. Isso ocorre porque, se o compilador souber exatamente que tipo a função retornará, também poderá otimizar o site de chamada da função. (Por exemplo, não verificando conversões de tipo.)

Chris Smith
fonte
Cerca de 99,999% dos aplicativos disponíveis no mercado têm muito mais coisas que diminuem a velocidade dos programas, como acesso a banco de dados, acesso a arquivos ou latência de rede. Pensar na velocidade ao projetar métodos pode ser uma razão válida para jogos, aplicativos em tempo real ou relatórios com toneladas de dados, mas não em outros casos. No entanto, é um bom argumento, mas, por menor que seja como programador, raramente preciso fazer esse tipo de otimização em meus aplicativos.
Samuel
Você faz esse tipo de coisa quando mede a velocidade do seu código e o acha muito lento, se sabe o que está fazendo (não é tão simples quanto você pensa) e se mede depois, e a velocidade melhorou .
gnasher729 10/11
3

Depende muito do conteúdo do código.

Vi uma rotina de mil linhas com as quais não tive problemas. Era uma declaração enorme de switch, nenhuma opção excedia uma dúzia de linhas e a única estrutura de controle em qualquer opção era um único loop. Hoje em dia teria sido escrito com objetos, mas essa não era uma opção naquela época.

Também estou olhando para 120 linhas em um interruptor na minha frente. Nenhum caso excede 3 linhas - um guarda, uma tarefa e o intervalo. Está analisando um arquivo de texto, objetos não são uma possibilidade. Qualquer alternativa seria mais difícil de ler.

Loren Pechtel
fonte
2

A maioria dos compiladores não se importa com o comprimento de uma função. Uma função deve ser funcional, mas fácil de entender, mudar e reutilizar para os seres humanos. Escolha um comprimento que melhor lhe convier.

LennyProgrammers
fonte
1

Minha regra geral é que uma função deve caber na tela. Descobri apenas três casos que tendem a violar isso:

1) Funções de expedição. Nos velhos tempos, isso era comum, mas a maioria deles é substituída pela herança de objetos atualmente. Porém, os objetos funcionam apenas dentro do seu programa e, portanto, você ainda verá funções de envio ocasionais ao lidar com dados que chegam de outro lugar.

2) Funções que executam várias etapas para atingir uma meta e onde as etapas não possuem uma boa subdivisão. Você acaba com uma função que simplesmente chama uma longa lista de outras funções em ordem.

3) Como no 2, mas onde as etapas individuais são tão pequenas que são simplesmente incorporadas ao invés de chamadas separadamente.

Loren Pechtel
fonte
1

Talvez o comprimento da função não seja uma boa métrica. Tentamos usar a complexidade ciclomática , também nos métodos, e um dos controles futuros de controle de fonte determina que a complexidade ciclomática em classes e métodos deve ser menor que X.

Para os métodos, X é definido como 30, e isso é bastante restrito.

Peter Mortensen
fonte