Na programação orientada a objetos, é claro que não existe uma regra exata sobre o tamanho máximo de um método, mas eu ainda encontrei essas duas aspas que se contradizem, de modo que gostaria de ouvir o que você pensa.
Em Código Limpo: Um Manual de Artesanato em Software Ágil , Robert Martin diz:
A primeira regra das funções é que elas devem ser pequenas. A segunda regra das funções é que elas devem ser menores que isso. As funções não devem ter 100 linhas. As funções quase nunca devem ter 20 linhas.
e ele dá um exemplo do código Java que ele vê de Kent Beck:
Todas as funções do programa tinham apenas duas, três ou quatro linhas. Cada um era claramente óbvio. Cada um contou uma história. E cada um levou você ao próximo em uma ordem convincente. É assim que suas funções devem ser curtas!
Isso parece ótimo, mas por outro lado, no Code Complete , Steve McConnell diz algo muito diferente:
Deve-se permitir que a rotina cresça organicamente até 100-200 linhas, décadas de evidência dizem que rotinas com esse tamanho não são mais propensas a erros do que rotinas mais curtas.
E ele faz referência a um estudo que diz que rotinas com 65 linhas ou mais são mais baratas de desenvolver.
Portanto, embora haja opiniões divergentes sobre o assunto, existe uma prática recomendada para você?
fonte
switch
declaração com 100case
condições é mais sustentável que 10 níveis deif
declarações aninhadas uma na outra.Respostas:
As funções normalmente devem ser curtas, entre 5-15 linhas é minha "regra de ouro" pessoal ao codificar em Java ou C #. Esse é um bom tamanho por vários motivos:
Mas não acho útil definir uma regra absoluta, pois sempre haverá exceções / razões válidas para divergir da regra:
Então, basicamente, use o bom senso , atenha-se a pequenos tamanhos de função na maioria dos casos, mas não seja dogmático se você tiver um motivo genuinamente bom para criar uma função extraordinariamente grande.
fonte
Embora eu concorde com os comentários de outros quando eles disseram que não há uma regra rígida sobre o número LOC correto, aposto que se olharmos para os projetos que vimos no passado e identificarmos todas as funções acima, digamos 150 linhas de código, eu ' Acho que chegamos a um consenso de que 9 em cada 10 dessas funções quebram o SRP (e provavelmente o OCP também), têm muitas variáveis locais, muito fluxo de controle e geralmente são difíceis de ler e manter.
Portanto, embora o LOC possa não ser um indicador direto de código incorreto, é certamente um indicador indireto decente que determinada função possa ser melhor escrita.
Na minha equipe, caí na posição de líder e, por qualquer motivo, as pessoas parecem estar me ouvindo. Em geral, decidi dizer à equipe que, embora não haja limite absoluto, qualquer função com mais de 50 linhas de código deve, no mínimo, levantar uma bandeira vermelha durante a revisão do código, para que possamos dar uma segunda olhada e reavaliar por complexidade e violações de SRP / OCP. Após esse segundo olhar, podemos deixá-lo em paz ou mudar, mas pelo menos isso faz as pessoas pensarem sobre essas coisas.
fonte
Entrei em um projeto que não teve nenhum cuidado com as diretrizes de codificação. Quando olho para o código, às vezes encontro classes com mais de 6000 linhas de código e menos de 10 métodos. Este é um cenário de horror quando você precisa corrigir bugs.
Uma regra geral de quão grande um método deve ter no máximo às vezes não é tão boa. Gosto da regra de Robert C. Martin (tio Bob): "Os métodos devem ser pequenos, menores que pequenos". Eu tento usar essa regra o tempo todo. Estou tentando manter meus métodos simples e pequenos, esclarecendo que meu método faz apenas uma coisa e nada mais.
fonte
Não se trata de número de linhas, é de SRP. De acordo com esse princípio, seu método deve fazer uma e apenas uma coisa.
Se o seu método faz isso AND this AND this OR that => provavelmente está fazendo muito. Tente analisar esse método e analisar: "aqui eu recebo esses dados, os classifico e obtemos os elementos necessários" e "aqui eu processo esses elementos" e "aqui eu finalmente os combino para obter o resultado". Esses "blocos" devem ser refatorados para outros métodos.
Se você simplesmente seguir o SRP, grande parte do seu método será pequeno e com clara intenção.
Não é correto dizer "este método é> 20 linhas, portanto está errado". Pode ser uma indicação de que algo pode estar errado com esse método, não mais.
Você pode ter um switch de 400 linhas em um método (geralmente acontece em telecomunicações), e ainda é de responsabilidade única e está perfeitamente bem.
fonte
Depende , sério, realmente não há uma resposta sólida para essa pergunta, porque a linguagem com a qual você trabalha trabalha, as cinco a décima quinta linhas mencionadas nesta resposta podem funcionar para C # ou Java, mas em outras linguagens ela não fornece você tem muito para trabalhar. Da mesma forma, dependendo do domínio em que você está trabalhando, você pode escrever valores de configuração de código em uma grande estrutura de dados. Com algumas estruturas de dados, você pode ter dezenas de elementos que precisa definir, você deve separar as coisas para separar funções apenas porque sua função está demorando muito?
Como outros observaram, a melhor regra geral é que uma função deve ser uma única entidade lógica que lida com uma única tarefa. Se você tentar impor regras draconianas que dizem que as funções não podem exceder n linhas e você tornar esse valor muito pequeno, seu código ficará mais difícil de ler à medida que os desenvolvedores tentarem usar truques sofisticados para contornar a regra. Da mesma forma, se você definir um valor muito alto, isso não será um problema e poderá resultar em código incorreto, embora preguiçoso. Sua melhor aposta é apenas realizar revisões de código para garantir que as funções estejam lidando com uma única tarefa e deixar assim.
fonte
Acho que um problema aqui é que o comprimento de uma função não diz nada sobre sua complexidade. LOC (linhas de código) são um instrumento ruim para medir qualquer coisa.
Um método não deve ser excessivamente complexo, mas há cenários em que um método longo pode ser facilmente mantido. Observe que o exemplo a seguir não diz que não pode ser dividido em métodos, apenas que os métodos não alterariam a capacidade de manutenção.
por exemplo, um manipulador de dados recebidos pode ter uma declaração de chave grande e, em seguida, código simples por caso. Eu tenho esse código - gerenciando dados recebidos de um feed. 70 (!) Manipuladores numericamente codificados. Agora, alguém dirá "use constantes" - sim, exceto que a API não as fornece e eu gosto de ficar perto da "fonte" aqui. Métodos? Claro - infelizmente, todos eles lidam com dados das mesmas duas grandes estruturas. Não há benefício em separá-los, exceto talvez com mais métodos (legibilidade). O código não é intrinsecamente complexo - uma opção, dependendo de um campo. Então, todo caso tem um bloco que analisa x elementos de dados e os publica. Sem pesadelo de manutenção. Existe uma condição "if" repetitiva que determina se um campo possui dados (pField = pFields [x], se pField-> IsSet () {blabla}) - o mesmo para todos os campos.
Substitua isso por uma rotina muito menor, contendo loop aninhado e muitas instruções de comutação reais, e um método enorme pode ser mais fácil de manter do que um menor.
Então, desculpe, o LOC não é uma boa medida para começar. Se for o caso, devem ser usados pontos de complexidade / decisão.
fonte
Vou apenas colocar mais uma citação.
É muito improvável que funções que crescem para 100-200 sigam esta regra
fonte
Estou nessa raquete louca, de um jeito ou de outro, desde 1970.
Durante todo esse tempo, com duas exceções que abordarei em breve, NUNCA vi uma "rotina" bem projetada (método, procedimento, função, sub-rotina, qualquer que seja) que PRECISAVA ser mais de uma página impressa ( cerca de 60 linhas). A grande maioria deles era bastante curta, da ordem de 10 a 20 linhas.
No entanto, eu vi MUITO código de "fluxo de consciência", escrito por pessoas que aparentemente nunca ouviram falar em modularização.
As duas exceções foram casos muito especiais. Um é, na verdade, uma classe de casos de exceção, que eu agrupo: grandes autômatos de estado finito, implementados como grandes declarações de comutação feias, geralmente porque não há uma maneira mais limpa de implementá-las. Essas coisas geralmente aparecem em equipamentos de teste automatizados, analisando os registros de dados do dispositivo em teste.
O outro era a rotina de torpedos de fótons do jogo STARTRK Matuszek-Reynolds-McGehearty-Cohen, escrito no CDC 6600 FORTRAN IV. Ele teve que analisar a linha de comando e simular o vôo de cada torpedo, com perturbações, verificar a interação entre o torpedo e cada tipo de coisa que ele poderia atingir, e, sim, simular a recursão para fazer conectividade de 8 vias em cadeias de novae de torpedear uma estrela que estava ao lado de outras estrelas.
fonte
Se eu encontrar um método longo, posso apostar que esse método não é devidamente testado em unidade ou, na maioria das vezes, não possui teste de unidade. Se você começar a fazer TDD, nunca criará métodos de 100 linhas com 25 responsabilidades diferentes e 5 loops aninhados. Os testes obrigam você a refatorar constantemente sua bagunça e escrever o código limpo do tio Bob.
fonte
Não há regras absolutas sobre o comprimento do método, mas as seguintes regras foram úteis:
fonte
Os autores entendem o mesmo por "função" e "rotina"? Normalmente, quando digo "função", quero dizer uma sub-rotina / operação que retorna um valor e "procedimento" para um que não retorna (e cuja chamada se torna uma única instrução). Essa não é uma distinção comum em todo o SE no mundo real, mas eu a vi nos textos dos usuários.
De qualquer maneira, não há resposta certa para isso. A preferência por um ou outro (se houver alguma preferência) é algo que eu esperaria ser muito diferente entre idiomas, projetos e organizações; assim como acontece com todas as convenções de código.
O único ponto que eu acrescentaria é que toda a afirmação "operações longas não é mais propensa a erros do que operações curtas" não é estritamente verdadeira. Além do fato de que mais código equivale a mais espaço potencial de erro, é óbvio que a divisão do código em segmentos tornará os erros mais fáceis de evitar e mais fáceis de localizar. Caso contrário, não haveria razão para dividir o código em pedaços, exceto a repetição. Mas isso talvez seja verdade apenas se os referidos segmentos forem documentados suficientemente bem para que você possa determinar os resultados de uma chamada de operação sem ler ou rastrear o código real (projeto por contrato com base em especificações, em vez de dependência concreta entre áreas do código).
Além disso, se você deseja que operações mais longas funcionem bem, convém adotar convenções de código mais rígidas para apoiá-las. Jogar uma declaração de retorno no meio de uma operação pode ser bom para uma operação curta, mas em operações mais longas isso pode criar uma grande seção de código que é condicional, mas não obviamente condicional, em uma rápida leitura (apenas por um exemplo).
Então, eu pensaria que qual estilo é menos provável de ser um pesadelo cheio de erros dependeria em grande parte das convenções que você adere ao restante do seu código. :)
fonte
IMHO, você não precisa usar a barra de rolagem para ler sua função. Assim que você precisar mover a barra de rolagem, levará mais tempo para entender como funciona a função.
Portanto, depende do ambiente de programação usual do trabalho em equipe (resolução da tela, editor, tamanho da fonte, etc ...). Nos anos 80, eram 25 linhas e 80 colunas. Agora, no meu editor, mostro quase 50 linhas. O número de colunas que exibi não foi alterado desde que dividi minha tela em duas para exibir dois arquivos por vez.
Em resumo, depende da configuração de seus colegas de trabalho.
fonte
Acho que a resposta da TomTom chegou perto de como eu me sinto sobre isso.
Mais e mais eu me pego mais na complexidade ciclomática do que nas linhas.
Eu normalmente viso não mais do que uma estrutura de controle por método, com exceção de quantos loops forem necessários para lidar com uma matriz multidimensional.
Às vezes me pego colocando ifs de uma linha em casos de troca, porque, por algum motivo, esses tendem a ser casos em que a divisão é mais difícil do que ajuda.
Observe que eu não conto a lógica de proteção contra esse limite.
fonte
No OOP, todas as coisas são objetadas e existem esses recursos:
Quando você observa essas regras, seus métodos geralmente são pequenos, mas não existem regras para regras pequenas ou muito pequenas (por exemplo, 2-3 linhas). Um benefício do método pequeno (unidade pequena, por exemplo, método ou função) são:
fonte