Então, olhando ao redor mais cedo, notei alguns comentários sobre métodos longos serem uma prática ruim.
Não tenho certeza se sempre concordo que métodos longos são ruins (e gostariam da opinião de outras pessoas).
Por exemplo, eu tenho algumas visualizações do Django que processam um pouco os objetos antes de enviá-las para a visualização, um método longo são 350 linhas de código. Eu tenho meu código escrito para que ele lide com os parâmetros - classificando / filtrando o conjunto de consultas e, pouco a pouco, faz algum processamento nos objetos que minha consulta retornou.
Portanto, o processamento é principalmente agregação condicional, que possui regras complexas o suficiente que não podem ser feitas facilmente no banco de dados, portanto, algumas variáveis declaradas fora do loop principal são alteradas durante o loop.
variable_1 = 0
variable_2 = 0
for object in queryset :
if object.condition_condition_a and variable_2 > 0 :
variable 1+= 1
.....
...
.
more conditions to alter the variables
return queryset, and context
Então, de acordo com a teoria, devo fatorar todo o código em métodos menores, para que eu tenha o método view como tendo no máximo uma página.
No entanto, tendo trabalhado em várias bases de código no passado, às vezes acho que torna o código menos legível, quando você precisa pular constantemente de um método para o próximo, descobrindo todas as partes dele, mantendo o método mais externo em sua cabeça.
Acho que, com um método longo e bem formatado, é possível ver a lógica mais facilmente, pois ela não está sendo ocultada nos métodos internos.
Eu poderia fatorar o código em métodos menores, mas muitas vezes há um loop interno sendo usado para duas ou três coisas, por isso resultaria em código mais complexo, ou métodos que não fazem uma coisa, mas duas ou três (alternativamente Eu poderia repetir loops internos para cada tarefa, mas haverá um impacto no desempenho).
Existe um caso em que métodos longos nem sempre são ruins? Há sempre um caso para os métodos de escrita, quando eles serão usados apenas em um só lugar?
ATUALIZAÇÃO: Parece que eu fiz essa pergunta há mais de um ano.
Então, refatorei o código após a resposta (mista) aqui, dividi-o em métodos. É um aplicativo Django que recupera conjuntos complexos de objetos relacionados do banco de dados, portanto o argumento de teste está fora (provavelmente levaria a maior parte do ano para criar objetos relevantes para os casos de teste. Eu tenho um tipo "isso precisa ser feito ontem" ambiente de trabalho antes que alguém reclame). Corrigir bugs nessa parte do código é marginalmente mais fácil agora, mas não em massa.
antes :
#comment 1
bit of (uncomplicated) code 1a
bit of code 2a
#comment 2
bit of code 2a
bit of code 2b
bit of code 2c
#comment 3
bit of code 3
agora:
method_call_1
method_call_2
method_call_3
def method_1
bit of (uncomplicated) code 1a
bit of code 2a
def method_2
bit of code 2a
bit of code 2b
bit of code 2c
def method_3
bit of code 3
fonte
Respostas:
Não, métodos longos nem sempre são ruins.
No livro Código Completo , mede-se que métodos longos às vezes são mais rápidos e fáceis de escrever e não levam a problemas de manutenção.
De fato, o que é realmente importante é permanecer SECO e respeitar a separação de preocupações. Em algum momento, o cálculo será demorado, mas realmente não causará problemas no futuro.
No entanto, pela minha experiência pessoal, a maioria dos métodos longos costuma ter falta de preocupação. De fato, métodos longos são uma maneira fácil de detectar que algo PODE estar errado no código, e que um cuidado especial é necessário aqui ao fazer a revisão do código.
EDIT: À medida que os comentários são feitos, eu adiciono um ponto interessante à resposta. Na verdade, eu também verificaria as métricas de complexidade da função (NPATH, complexidade ciclomática ou CRAP ainda melhor).
Na verdade, eu recomendo não verificar essas métricas em funções longas, mas incluir alertas nelas com ferramentas automatizadas (como estilo de verificação para java, por exemplo) EM TODAS AS FUNÇÕES.
fonte
A maior parte do foco aqui parece estar sempre presente . Sim, absolutos são ruins, e a engenharia de software é quase tanto arte quanto ciência, e tudo isso ... mas eu vou ter que dizer que, para o exemplo que você deu, o método seria melhor se fosse dividido acima. Estes são os argumentos que eu normalmente usaria para justificar a divisão do seu método:
Legibilidade: Não tenho certeza dos outros, mas não consigo ler 350 linhas de código rapidamente. Sim, se é o meu próprio código, e eu posso fazer muitas suposições sobre isso, eu poderia examiná-lo muito rapidamente, mas isso está além do ponto. Considere como esse método seria mais fácil de ler, se consistisse em 10 chamadas para outros métodos (cada um com um nome descritivo). Ao fazer isso, você introduziu uma camada no código, e o método de alto nível fornece um esboço breve e agradável ao leitor sobre o que está acontecendo.
Editar - para colocar isso de uma maneira diferente, pense assim: como você explicaria esse método para um novo membro da equipe? Certamente, tem alguma estrutura que você pode resumir ao longo das linhas de "bem, começa fazendo A, depois B, depois às vezes C, etc". Ter um breve método de "visão geral" chamando outros métodos torna essa estrutura óbvia. É extremamente raro encontrar 350 linhas de código que não se beneficiam; se o cérebro humano não pretende lidar com listas de centenas de itens, nós os agrupamos.
Reutilização: métodos longos tendem a ter baixa coesão - geralmente fazem mais de uma coisa. A baixa coesão é inimiga da reutilização; se você combinar várias tarefas em um método, ele será reutilizado em menos lugares do que deveria.
Testabilidade e coesão: mencionei a complexidade ciclomática em um comentário acima - é uma boa medida de quão complexo é o seu método. Ele representa o limite inferior do número de caminhos exclusivos através do seu código, dependendo das entradas (editar: corrigido de acordo com o comentário de MichaelT). Isso também significa que, para testar seu método corretamente, você precisa ter pelo menos tantos casos de teste quanto o número de complexidade ciclomática. Infelizmente, quando você cria partes de código que não são realmente dependentes uma da outra, não há como ter certeza dessa falta de dependência, e a complexidade tende a se multiplicar. Você pode pensar nessa medida como uma indicação do número de coisas diferentes que você está tentando fazer. Se estiver muito alto, é hora de dividir e conquistar.
Refatoração e estrutura: métodos longos geralmente indicam que falta alguma estrutura no código. Freqüentemente, o desenvolvedor não conseguiu descobrir quais são os pontos em comum entre as diferentes partes desse método e onde uma linha pode ser traçada entre eles. Perceber que um método longo é um problema e tentar dividi-lo em métodos menores é o primeiro passo em um caminho mais longo para realmente identificar uma estrutura melhor para a coisa toda. Talvez você precise criar uma classe ou duas; não será necessariamente mais complexo no final!
Eu também acho que, neste caso, a desculpa para ter um método longo é "... algumas variáveis declaradas fora do loop principal são alteradas durante o loop". Não sou especialista em Python, mas tenho quase certeza de que esse problema pode ser corrigido trivialmente por alguma forma de passagem por referência.
fonte
someVar.toString()
e sentiu você precisava ver o código do toString para saber o que está fazendo? Você acabou de ler o passado por causa da boa nomeação de métodos.Métodos longos são sempre ruins, mas ocasionalmente são melhores que as alternativas.
fonte
Métodos longos são um cheiro de código . Eles geralmente indicam que algo está errado, mas não é uma regra rígida. Normalmente, os casos em que são justificados envolvem muitas regras de negócios estatais e bastante complexas (como você descobriu).
Quanto à sua outra pergunta, é frequentemente útil separar pedaços de lógica em métodos separados, mesmo que sejam chamados apenas uma vez. Isso facilita a visualização da lógica de alto nível e pode tornar a manipulação de exceções um pouco mais limpa. Contanto que você não precise passar vinte parâmetros para representar o estado do processamento!
fonte
Métodos longos nem sempre são ruins. Eles geralmente são um sinal de que pode haver um problema.
No sistema em que estou trabalhando, temos meia dúzia de métodos com mais de 10.000 linhas de comprimento. Um deles tem atualmente 54.830 linhas. E tudo bem.
Essas funções ridiculamente longas são muito simples e são geradas automaticamente. Esse grande monstro de 54.830 linhas contém os dados diários de movimento polar de 1 de janeiro de 1962 a 10 de janeiro de 2012 (nossa última versão). Também liberamos um procedimento pelo qual nossos usuários podem atualizar esse arquivo gerado automaticamente. Esse arquivo de origem contém os dados de movimento polar de http://data.iers.org/products/214/14443/orig/eopc04_08_IAU2000.62-now , traduzidos automaticamente para C ++.
Não é possível ler esse site em tempo real em uma instalação segura. Não há conexão com o mundo exterior. Baixar o site como uma cópia local e analisar em C ++ também não é uma opção; a análise é lenta e isso deve ser rápido. Download, tradução automática para C ++ e compilação: Agora você tem algo rápido. (Apenas não o compile otimizado. É incrível quanto tempo um compilador otimizador leva para compilar 50.000 linhas de código de linha reta extremamente simples. Leva mais de meia hora no meu computador para compilar esse arquivo otimizado. E a otimização não realiza absolutamente nada Não há nada para otimizar. É um código linear simples, uma declaração de atribuição após a outra.)
fonte
Digamos apenas que existem maneiras boas e ruins de interromper um método longo. Ter que "[manter] o método mais externo em sua cabeça" é um sinal de que você não o está quebrando da melhor maneira possível, ou que seus submétodos são mal nomeados. Em teoria, há casos em que um método longo é melhor. Na prática, é extremamente raro. Se você não conseguir descobrir como tornar um método mais curto legível, peça a alguém para revisar seu código e peça especificamente idéias sobre como encurtar os métodos.
Quanto a vários loops que causam um suposto impacto no desempenho, não há como saber isso sem medir. Vários loops menores podem ser significativamente mais rápidos se isso significa que tudo o que precisa pode permanecer no cache. Mesmo se houver um impacto no desempenho, geralmente é insignificante em favor da legibilidade.
Eu direi que geralmente os métodos longos são mais fáceis de escrever , embora sejam mais difíceis de ler . É por isso que eles proliferam, embora ninguém goste deles. Não há nada de errado em planejar desde o início a refatoração antes de fazer o check-in.
fonte
Métodos longos podem ser mais computacionais e eficientes em termos de espaço, pode ser mais fácil ver a lógica e mais fácil depurá-los. No entanto, essas regras só se aplicam quando apenas um programador toca nesse código. O código será muito difícil de estender se não for atômico; essencialmente, a próxima pessoa terá que começar do zero e, em seguida, depurar e testar isso levará uma eternidade, pois não está usando nenhum código válido.
fonte
Há algo que chamamos de Decomposição Funcional, que implica dividir seus métodos mais longos em métodos menores, sempre que possível. Como você mencionou que seu método envolve classificação / filtragem, é melhor você ter métodos ou funções separados para essas tarefas.
Precisamente, seu método deve se concentrar apenas na execução de 1 tarefa.
E se precisar chamar outro método para algum motivo, faça-o de outra forma com o que você já está escrevendo. Também para fins de legibilidade, você pode adicionar comentários. Convencionalmente, os programadores usam comentários de várias linhas (/ ** / em C, C ++, C # e Java) para descrições de métodos e usam comentários de linha única (// em C, C ++, C # e Java). Também existem boas ferramentas de documentação disponíveis para maior legibilidade do código (por exemplo, JavaDoc ). Você também pode pesquisar comentários baseados em XML se for um desenvolvedor .Net.
Os loops afetam o desempenho do programa e podem causar sobrecarga no aplicativo, se não forem utilizados adequadamente. A idéia é projetar seu algoritmo de forma que você use loops aninhados o mínimo possível.
fonte
É perfeitamente bom escrever funções demoradas. Mas isso varia no contexto, se você realmente precisa ou não. Por exemplo, alguns dos melhores algorthms são expressos melhor quando um pedaço. Por outro lado, uma grande porcentagem de rotinas em programas orientados a objetos serão rotinas de acessadores, que serão muito curtas. Algumas das rotinas de processamento demoradas que possuem casos de comutação longos, se as condições puderem ser otimizadas através de métodos orientados por tabela.
Há uma excelente discussão curta no Código Completo 2 sobre o tamanho das rotinas.
fonte
Outra votação que quase sempre está errada. No entanto, encontro dois casos básicos em que é a resposta adequada:
1) Um método que basicamente chama vários outros métodos e que não funciona em si. Você tem um processo que leva 50 etapas para realizar, você obtém um método com 50 chamadas. Geralmente, não há nada a ganhar tentando acabar com isso.
2) Despachantes. O design de POO se livrou da maioria desses métodos, mas as fontes de dados recebidas são, por sua própria natureza, apenas dados e, portanto, não podem seguir os princípios de POO. Não é exatamente incomum ter algum tipo de rotina de expedidor no código que manipula os dados.
Eu diria também que não se deve considerar a questão ao lidar com coisas geradas automaticamente. Ninguém está tentando entender o que o código gerado automaticamente, não importa se é fácil para um ser humano entender.
fonte
Eu queria abordar o exemplo que você deu:
Na minha empresa, nosso maior projeto é construído no Django e também temos funções de visualização longa (muitas são mais de 350 linhas). Eu diria que a nossa não precisa demorar tanto, e eles estão nos machucando.
Essas funções de exibição estão realizando um trabalho muito relacionado que deve ser extraído para o modelo, classes auxiliares ou funções auxiliares. Além disso, acabamos reutilizando visualizações para fazer coisas diferentes, que devem ser divididas em visualizações mais coesas.
Suspeito que suas opiniões tenham características semelhantes. No meu caso, sei que isso causa problemas e estou trabalhando para fazer alterações. Se você não concorda que está causando problemas, não precisa corrigi-lo.
fonte
Não sei se alguém já mencionou isso, mas uma das razões pelas quais os métodos longos são ruins é porque geralmente envolvem vários níveis diferentes de abstração. Você tem variáveis de loop e todos os tipos de coisas acontecendo. Considere a função fictícia:
Se você estivesse fazendo toda a animação, cálculo, acesso a dados etc. nessa função, seria uma bagunça. A função nextSlide () mantém uma camada de abstração consistente (o sistema de estado do slide) e ignora outras. Isso torna o código legível.
Se você precisar constantemente usar métodos menores para ver o que eles fazem, o exercício de dividir a função falhará. Só porque o código que você está lendo não está fazendo coisas óbvias nos métodos filhos não significa que os métodos filhos sejam uma má ideia, apenas que foi feita incorretamente.
Quando crio métodos, geralmente acabo dividindo-os em métodos menores, como uma espécie de estratégia de dividir e conquistar. Um método como
é certamente mais legível do que
Direito?
Concordo que afirmações absolutas são ruins e também concordo que geralmente um método longo é ruim.
fonte
História real. Certa vez, encontrei um método com mais de duas mil linhas. O método tinha regiões que descreviam o que estava fazendo nessas regiões. Depois de ler uma região, decidi fazer um método de extração automatizada, nomeando-o de acordo com o nome da região. quando terminei, o método não passava de 40 chamadas de método, com cerca de cinquenta linhas cada, e tudo funcionava da mesma maneira.
O que é muito grande é subjetivo. Às vezes, um método não pode ser dividido além do que é atualmente. É como escrever um livro. A maioria das pessoas concorda que parágrafos longos geralmente devem ser divididos. Mas, às vezes, há apenas uma idéia e dividi-la causa mais confusão do que seria causado pelo comprimento desse parágrafo.
fonte
O objetivo de um método é ajudar a reduzir a regurgitação de código. Um método deve ter uma função específica pela qual é responsável. Se você acabar reformulando o código em vários locais, corre o risco de obter resultados inesperados se as especificações que o software foi projetado para atender forem alteradas.
Para um método ter 350 linhas, sugeriria que muitas das tarefas que ele está executando são replicadas em outros lugares, pois é incomum exigir uma quantidade tão grande de código para executar uma tarefa especializada.
fonte
Na verdade, não são os métodos longos que são más práticas, é mais deixá-los assim.
Quero dizer que o ato real de refatorar sua amostra de:
para
e depois para
Agora você está no caminho certo, não apenas para um método muito mais curto, mas para um método muito mais utilizável e compreensível.
fonte
Eu acho que o fato de o método ser muito longo é algo a ser verificado, mas definitivamente não é um antipadrão instantâneo. A grande coisa a procurar em métodos enormes é muito aninhamento. Se você tem
e o corpo do loop não é extremamente localizado (ou seja, você pode enviar menos de quatro parâmetros), provavelmente é melhor convertê-lo para:
Por sua vez, isso pode reduzir bastante o comprimento de uma função. Além disso, procure um código duplicado na função e mova-o para uma função separada
Finalmente, alguns métodos são longos e complicados e não há nada que você possa fazer. Alguns problemas precisam de soluções que não sejam fáceis de codificar. Por exemplo, analisar uma gramática de muita complexidade pode criar métodos realmente longos que você realmente não pode fazer muito sem piorá-lo.
fonte
A verdade é que depende. Como mencionado, se o código não separar preocupações e tentar fazer tudo em um método, será um problema. A separação do código em vários módulos facilita a leitura do código e a gravação do código (por vários programadores). Aderir a um módulo (classe) por arquivo de origem é uma boa idéia para começar.
Em segundo lugar, quando se trata de funções / procedimentos:
é um bom método se verificar apenas o intervalo de "Dados". É um método MAU quando o mesmo intervalo se aplica a várias funções (exemplo de código incorreto):
Isso deve ser refatorado para:
REUTILIZE o código o máximo possível. E isso é possível quando cada função do seu programa é SIMPLES (não necessariamente fácil) o suficiente.
CITAÇÃO: A MONTANHA é composta de pequenos grãos de terra. O oceano é composto de pequenas gotas de água .. (- Sivananda)
fonte
Métodos longos tendem a ser "ruins" em linguagens imperativas que favorecem afirmações, efeitos colaterais e mutabilidade, precisamente porque esses recursos aumentam a complexidade e, portanto, os erros.
Nas linguagens de programação funcionais, que favorecem expressões, pureza e imutabilidade, há menos motivos para preocupação.
Em linguagens funcionais e imperativas, é sempre melhor levar em consideração trechos reutilizáveis de código em rotinas comuns de nível superior, mas em linguagens funcionais que suportam escopo lexical com funções aninhadas etc., é realmente melhor encapsulamento para ocultar sub-rotinas na parte superior função de nível superior (método) do que dividi-las em outras funções de nível superior.
fonte