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.
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.
Respostas:
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.
Depende do idioma que você usa, mas em geral (e para o meu gosto pessoal):
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
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:
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
tt
para 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.
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)
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".
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.
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.
Contanto que ele precise fazer o que precisa, mas não mais.
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 ...
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:
Eu raramente escrevo funções por mais tempo que isso. A maioria delas são instruções de comutação C / C ++ gigantes.
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.
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.
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.
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.
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.)
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.
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.
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.
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.