35 linhas, 55 linhas, 100 linhas, 300 linhas? Quando você deve começar a separá-lo? Estou perguntando porque tenho uma função com 60 linhas (incluindo comentários) e estava pensando em desmembrá-la.
long_function(){ ... }
para dentro:
small_function_1(){...}
small_function_2(){...}
small_function_3(){...}
As funções não serão usadas fora da função long_, tornando funções menores significa mais chamadas de função etc.
Quando você dividiria uma função em outras menores? Por quê?
- Os métodos devem fazer apenas uma coisa lógica (pense na funcionalidade)
- Você deve ser capaz de explicar o método em uma única frase
- Deve caber na altura do seu monitor
- Evite despesas desnecessárias (comentários que apontam para o óbvio ...)
- O teste de unidade é mais fácil para pequenas funções lógicas
- Verifique se parte da função pode ser reutilizada por outras classes ou métodos
- Evite acoplamentos inter-classes excessivos
- Evite estruturas de controle profundamente aninhadas
Obrigado a todos pelas respostas , edite a lista e vote na resposta correta; eu vou escolher essa;)
Estou refatorando agora com essas idéias em mente :)
function
refactoring
coding-style
user58163
fonte
fonte
Respostas:
Não há regras reais e rígidas para isso. Geralmente, gosto dos meus métodos para "fazer uma coisa". Portanto, se estiver capturando dados, fazendo alguma coisa com esses dados e depois gravando-os em disco, eu dividiria a captura e a gravação em métodos separados, para que o meu método "principal" contenha apenas "fazer alguma coisa".
Como "fazer alguma coisa" ainda pode ter algumas linhas, por isso não tenho certeza de que várias linhas sejam a métrica certa para usar :)
Edit: Esta é uma única linha de código que enviei pelo correio na semana passada (para provar um ponto ... não é algo que eu tenha o hábito :)) - eu certamente não iria querer de 50 a 60 desses bandidos no meu método : D
fonte
Aqui está uma lista de bandeiras vermelhas (em nenhuma ordem específica) que podem indicar que uma função é muito longa:
Estruturas de controle profundamente aninhadas : por exemplo, for-loops com 3 níveis de profundidade ou até apenas 2 níveis de profundidade com instruções if aninhadas que possuem condições complexas.
Muitos parâmetros de definição de estado : Por parâmetro de definição de estado , quero dizer um parâmetro de função que garante um caminho de execução específico através da função. Obtenha muitos desses tipos de parâmetros e você terá uma explosão combinatória dos caminhos de execução (isso geralmente acontece em conjunto com o nº 1).
Lógica duplicada em outros métodos : a má reutilização de código é um grande contribuinte para o código processual monolítico. Muitas dessas duplicações lógicas podem ser muito sutis, mas uma vez re-fatoradas, o resultado final pode ser um design muito mais elegante.
Acoplamento inter-classe excessivo : essa falta de encapsulamento adequado resulta em funções preocupadas com características íntimas de outras classes, prolongando-as.
Sobrecarga desnecessária : comentários que apontam classes óbvias e profundamente aninhadas, getters e setters supérfluos para variáveis de classe aninhadas privadas e nomes de funções / variáveis invulgarmente longos podem criar ruído sintático nas funções relacionadas que, em última análise, aumentarão seu tamanho.
Sua enorme tela de nível de desenvolvedor não é grande o suficiente para exibi-la : na verdade, as telas de hoje são grandes o suficiente para que uma função que esteja próxima da sua altura seja provavelmente muito longa. Mas, se for maior , é uma arma de fumo que algo está errado.
Você não pode determinar imediatamente propósito da função : Além disso, uma vez que você realmente fazer determinar a sua finalidade, se você não pode resumir este fim em uma única frase ou acontecer de ter uma tremenda dor de cabeça, esta deve ser uma pista.
Em conclusão, as funções monolíticas podem ter conseqüências de longo alcance e geralmente são um sintoma de grandes deficiências de design. Sempre que encontro um código que é uma alegria absoluta de ler, sua elegância é imediatamente aparente. E adivinhe: as funções geralmente são muito curtas.
fonte
// fetch Foo's credentials where Bar is "uncomplete"
. Esse é quase certamente um nome de função e deve ser dissociado. Provavelmente quer ser refatorado para algo como:Foo.fetchCredentialWhereBarUncomplete()
Acho que há uma grande ressalva no mantra "faça apenas uma coisa" nesta página. Às vezes, fazer uma coisa faz malabarismos com muitas variáveis. Não divida uma função longa em várias funções menores se as funções menores tiverem longas listas de parâmetros. Isso apenas transforma uma única função em um conjunto de funções altamente acopladas, sem nenhum valor individual real.
fonte
Uma função deve fazer apenas uma coisa. Se você estiver fazendo muitas coisas pequenas em uma função, torne cada uma delas uma função e chame essas funções da função longa.
O que você realmente não quer fazer é copiar e colar a cada 10 linhas da sua função longa em funções curtas (como sugere o seu exemplo).
fonte
Concordo que uma função deve fazer apenas uma coisa, mas em que nível é essa coisa.
Se suas 60 linhas estão realizando uma coisa (da perspectiva de seus programas) e as peças que compõem essas 60 linhas não serão usadas por mais nada, então 60 linhas é bom.
Não há nenhum benefício real em desmembrá-lo, a menos que você possa desmembrá-lo em pedaços de concreto que são autônomos. A métrica a ser usada é a funcionalidade e não as linhas de código.
Trabalhei em muitos programas em que os autores levaram a única coisa a um nível extremo e tudo o que acabou fazendo foi fazer parecer que alguém levou uma granada para uma função / método e a explodiu em dezenas de peças não conectadas que são difícil de seguir.
Ao retirar partes dessa função, você também precisa considerar se estará adicionando uma sobrecarga desnecessária e evite transmitir grandes quantidades de dados.
Acredito que o ponto principal é procurar reutilização nessa função longa e retirar essas peças. O que resta é a função, seja ela de 10, 20 ou 60 linhas.
fonte
60 linhas é grande, mas não muito longa para uma função. Se ele se encaixa em uma tela de um editor, você pode ver tudo de uma vez. Realmente depende do que as funções estão fazendo.
Por que eu posso dividir uma função:
fonte
DoThisAndThisAndAlsoThis
função, mas com um monte de abstração que você ainda tem que nomear alguma formaMinha heurística pessoal é que é muito longo se eu não conseguir ver a coisa toda sem rolar.
fonte
Tamanho aproximado do tamanho da tela (então pegue um widescreen dinâmico grande e gire-o) ... :-)
Brincadeira à parte, uma coisa lógica por função.
E o positivo é que o teste de unidade é realmente muito mais fácil de fazer com pequenas funções lógicas que fazem uma coisa. Grandes funções que fazem muitas coisas são mais difíceis de verificar!
/ Johan
fonte
Regra geral: se uma função contiver blocos de código que fazem algo, que é um pouco separado do restante do código, coloque-o em uma função separada. Exemplo:
muito melhor:
Essa abordagem tem duas vantagens:
Sempre que você precisar buscar endereços para um determinado código postal, poderá usar a função prontamente disponível.
Quando você precisar ler a função
build_address_list_for_zip()
novamente, você sabe o que o primeiro bloco de código fará (ele busca endereços para um determinado código postal, pelo menos é o que você pode derivar do nome da função). Se você tivesse deixado o código de consulta em linha, primeiro seria necessário analisá-lo.[Por outro lado (negarei ter dito isso a você, mesmo sob tortura): se você ler muito sobre otimização de PHP, poderá ter a idéia de manter o número de funções o menor possível, porque a chamada de função é muito, muito caro em PHP. Não sei disso, pois nunca fiz benchmarks. Se for esse o caso, você provavelmente não seguirá nenhuma das respostas da sua pergunta se o aplicativo for muito "sensível ao desempenho" ;-)]
fonte
Dê uma olhada na ciclomática de McCabe, na qual ele divide seu código em um gráfico onde: "Cada nó no gráfico corresponde a um bloco de código no programa em que o fluxo é seqüencial e os arcos correspondem às ramificações obtidas no programa. "
Agora imagine que seu código não possui funções / métodos; é apenas uma enorme expansão de código na forma de um gráfico.
Você deseja dividir essa expansão em métodos. Considere que, quando o fizer, haverá um certo número de blocos em cada método. Apenas um bloco de cada método estará visível para todos os outros métodos: o primeiro bloco (presumimos que você poderá pular para um método em apenas um ponto: o primeiro bloco). Todos os outros blocos em cada método serão informações ocultas nesse método, mas cada bloco dentro de um método pode potencialmente pular para qualquer outro bloco dentro desse método.
Para determinar qual o tamanho dos seus métodos em termos de número de blocos por método, uma pergunta que você deve fazer é: quantos métodos eu devo ter para minimizar o número potencial máximo de dependências (MPE) entre todos os blocos?
Essa resposta é dada por uma equação. Se r é o número de métodos que minimiza o MPE do sistema en é o número de blocos no sistema, a equação é: r = sqrt (n)
E pode ser mostrado que isso fornece o número de blocos por método como sendo também sqrt (n).
fonte
Lembre-se de que você pode refazer o fatorial apenas por causa do fatorial, potencialmente tornando o código mais ilegível do que era em primeiro lugar.
Um ex-colega meu tinha uma regra bizarra de que uma função / método deve conter apenas 4 linhas de código! Ele tentou se ater a isso com tanta rigidez que seus nomes de métodos frequentemente se tornaram repetitivos e sem sentido, além de as chamadas se tornarem profundamente aninhadas e confusas.
Então, meu próprio mantra se tornou: se você não consegue pensar em um nome de função / método decente para o pedaço de código que está refatorando, não se preocupe.
fonte
A principal razão pela qual costumo interromper uma função é porque pedaços dela também são ingredientes de outra função próxima que estou escrevendo, para que as partes comuns sejam fatoradas. Além disso, se estiver usando muitos campos ou propriedades de alguma outra classe, há uma boa chance de que o pedaço relevante possa ser retirado por atacado e, se possível, movido para a outra classe.
Se você tiver um bloco de código com um comentário na parte superior, considere puxá-lo para uma função, com os nomes das funções e dos argumentos ilustrando sua finalidade e reservando o comentário para a lógica do código.
Tem certeza de que não há peças que seriam úteis em outros lugares? Que tipo de função é essa?
fonte
Na minha opinião, a resposta é: quando faz muitas coisas. Sua função deve executar apenas as ações que você espera do nome da própria função. Outra coisa a considerar é se você deseja reutilizar algumas partes de suas funções em outras; nesse caso, pode ser útil dividi-lo.
fonte
Eu costumo dividir funções pela necessidade de colocar comentários descrevendo o próximo bloco de código. O que anteriormente entrou nos comentários agora passa para o novo nome da função. Esta não é uma regra difícil, mas (para mim) uma boa regra de ouro. Eu gosto de falar por código melhor do que aquele que precisa de comentários (como eu aprendi que os comentários geralmente mentem)
fonte
Isso é parcialmente uma questão de gosto, mas como eu determino isso é que eu tento manter minhas funções aproximadamente apenas enquanto caber na tela ao mesmo tempo (no máximo). A razão é que é mais fácil entender o que está acontecendo, se você pode ver tudo ao mesmo tempo.
Quando eu codifico, é uma mistura de escrever funções longas e refatorar para extrair bits que poderiam ser reutilizados por outras funções - e escrever pequenas funções que executam tarefas discretas à medida que eu passo.
Não sei se há uma resposta certa ou errada para isso (por exemplo, você pode escolher 67 linhas como o máximo, mas pode haver momentos em que faz sentido adicionar mais algumas).
fonte
Houve uma pesquisa minuciosa feita sobre esse mesmo tópico; se você deseja o menor número de erros, seu código não deve ser muito longo. Mas também não deve ser muito curto.
Eu discordo que um método deve caber em sua exibição em um, mas se você estiver rolando a página para baixo em mais de uma página, o método será muito longo.
Consulte O tamanho ideal da classe para software orientado a objetos para uma discussão mais aprofundada.
fonte
Eu escrevi 500 funções de linha antes, no entanto, essas eram apenas grandes instruções de chave para decodificar e responder a mensagens. Quando o código de uma única mensagem ficou mais complexo que um único if-then-else, eu o extraí.
Em essência, embora a função fosse de 500 linhas, as regiões mantidas independentemente tiveram em média 5 linhas.
fonte
Normalmente, eu uso uma abordagem orientada a testes para escrever código. Nessa abordagem, o tamanho da função geralmente está relacionado à granularidade de seus testes.
Se o seu teste estiver suficientemente focado, ele o levará a escrever uma pequena função focada para fazer o teste passar.
Isso também funciona na outra direção. As funções precisam ser pequenas o suficiente para testar efetivamente. Portanto, ao trabalhar com código legado, geralmente descubro funções maiores para testar as diferentes partes delas.
Geralmente me pergunto "qual é a responsabilidade dessa função" e se não posso declarar a responsabilidade em uma sentença clara e concisa e depois a traduzo em um pequeno teste focado, me pergunto se a função é muito grande.
fonte
Se tiver mais de três ramificações, geralmente isso significa que uma função ou método deve ser desmembrado, para encapsular a lógica de ramificação em diferentes métodos.
Cada instrução for loop, if, etc não é vista como uma ramificação no método de chamada.
O Cobertura para código Java (e tenho certeza de que existem outras ferramentas para outras linguagens) calcula o número de if, etc. em uma função para cada função e o soma para a "complexidade ciclomática média".
Se uma função / método tiver apenas três ramificações, obterá três nessa métrica, o que é muito bom.
Às vezes, é difícil seguir esta diretriz, nomeadamente para validar a entrada do usuário. No entanto, a inserção de ramificações em diferentes métodos ajuda não apenas ao desenvolvimento e manutenção, mas também ao teste, já que as entradas dos métodos que executam a ramificação podem ser analisadas facilmente para ver quais entradas precisam ser adicionadas aos casos de teste para cobrir as ramificações que não foram cobertos.
Se todas as ramificações estivessem dentro de um único método, as entradas teriam que ser rastreadas desde o início do método, o que dificulta a testabilidade.
fonte
Eu suspeito que você encontrará muitas respostas sobre isso.
Provavelmente, eu o dividiria com base nas tarefas lógicas que estavam sendo executadas dentro da função. Se você achar que seu conto está se transformando em romance, sugiro que encontre e extraia etapas distintas.
Por exemplo, se você tem uma função que lida com algum tipo de entrada de string e retorna um resultado de string, você pode dividir a função com base na lógica para dividir sua string em partes, a lógica para adicionar caracteres extras e a lógica para colocá-la todos juntos novamente como um resultado formatado.
Em resumo, o que torna seu código limpo e fácil de ler (seja simplesmente garantindo que sua função tenha bons comentários ou desmembrando) é a melhor abordagem.
fonte
supondo que você esteja fazendo uma coisa, o comprimento dependerá:
60 linhas podem ser muito longas ou podem estar certas. Eu suspeito que pode demorar demais.
fonte
Uma coisa (e essa coisa deve ser óbvia no nome da função), mas não mais do que uma tela cheia de código, independentemente. E fique à vontade para aumentar o tamanho da fonte. E em caso de dúvida, refatorá-lo em duas ou mais funções.
fonte
Estendendo o espírito de um tweet do tio Bob há um tempo, você sabe que uma função está ficando muito longa quando você sente a necessidade de colocar uma linha em branco entre duas linhas de código. A idéia é que, se você precisar de uma linha em branco para separar o código, sua responsabilidade e escopo estarão separados nesse ponto.
fonte
Minha idéia é que, se eu tiver que me perguntar se é muito longo, provavelmente é muito longo. Ajuda a criar funções menores nessa área, pois pode ajudar mais tarde no ciclo de vida do aplicativo.
fonte