Sempre que me pego escrevendo a mesma lógica mais de uma vez, costumo colocá-la em uma função para que haja apenas um lugar no meu aplicativo que eu tenha para manter essa lógica. Um efeito colateral é que às vezes acabo com uma ou duas funções de linha, como:
Isso é preguiçoso ou é uma prática ruim? Eu só pergunto porque isso resulta em um número maior de funções que exigem peças muito pequenas de lógica.
Não, não muito curto. Em C #, eu faria 'condição atendida' como uma propriedade, que é elegante e consideraria usar Assert.AreEqual<int>(expected, actual, message, arg1, arg2, arg3, ...);. O segundo está bem como está. Eu incluiria potencialmente um sinalizador bool opcional que ditaria se lançaria uma exceção / etc. caso o retorno de chamada não seja uma função.
Sim, em apenas um caso: function myFunction () {}
@Spooks - Estou dividindo os cabelos aqui, mas funções vazias são válidas para pelo menos classes de adaptadores, por exemplo, MouseMotionAdapter em download.oracle.com/javase/6/docs/api/java/awt/event/… . Obviamente, essa é apenas uma maneira de solucionar as limitações de idioma.
@dietbuddha: Os requisitos foram alterados - agora ele precisa suportar dois idiomas para uma demonstração na próxima semana. O cara que escreveu "def yes ()" se sentiu muito feliz por ter mudado para "def yes (: (IsFrench ()? 'Oui': 'Yes')"
Hehe, oh Sr. Brown, se eu pudesse convencer todos os desenvolvedores que encontro a manter suas funções tão pequenas quanto isso, acredite, o mundo do software seria um lugar melhor!
3) SECO - Não se repita - você está se adaptando muito bem a isso!
4) testável. As pequenas funções são um milhão de vezes mais fáceis de testar do que os métodos de 200 linhas que vemos com muita frequência.
Ah, e não se preocupe com o "salto de função" em termos de desempenho. As versões "Release" e as otimizações do compilador cuidam disso muito bem para nós, e o desempenho é 99% do tempo em outro lugar no design de sistemas.
Isso é uma prática ruim? - Absolutamente não. Melhor usar essa maneira de criar métodos do que as bolas de alcatrão ou "objetos de Deus", que são muito comuns.
Mantenha o bom trabalho, meu colega artesão;)
Você não está respondendo à pergunta, apenas está pregando a regra cujas aplicações mais extremas são questionadas aqui.
Não é muito frequente que eu ache a limitação de apenas 1 voto positivo frustrante. Mas, para esta resposta, o +1 não parece fazer justiça.
+5 ... oh, só posso fazer +1, tudo bem! E para dar suporte ao MeshMan, sugiro o seguinte livro de Robert C. Martin Código limpo . Este livro está realmente mudando a maneira como agora programo.
— Eric-Karl
Resposta muito agradável, mas discordo da maioria: 1) Funções extremamente pequenas resultam em mais código geral, devido a todo o encanamento necessário para cada função. Isso pode reduzir a legibilidade, especialmente para um projeto realmente grande. Além disso, depende do idioma. 2) Depende inteiramente do ponto 1 e, portanto, não tem nada a ver com a questão. 3) O que? runCallback repete "retorno de chamada" quatro vezes e três vezes se refere à mesma variável para executar uma única função. 4) É claro que mais de 200 métodos de linha não podem ser testados, mas existe um longo caminho até uma linha.
-1: A coisa mais assustadora aqui é o número de votos dados para esta resposta.
Duplica uma operação primitiva, sem outra finalidade senão torná-la um método:
O código é usado apenas uma vez e sua intenção é fácil de entender rapidamente.
Esse poderia ser um padrão válido, mas eu apenas incorporaria o método configureDialog (), neste exemplo, a menos que pretendesse substituí-lo ou reutilizar esse código em outro local.
— RMorrisey
Separar um método de linha única no segundo caso pode ser benéfico para manter consistente o nível de abstração dos métodos de chamada. O exemplo fornecido está em cima do muro se esse raciocínio for usado: a largura e a altura exatas dos pixels são muitos detalhes para mostrar uma caixa de diálogo?
— Aleksi Yrttiaho 02/04
O método "isNotValue ()" tem um nome incorreto e deve ser refatorado para nomear a questão real do domínio.
@ Aleksi: eu discordo; os detalhes já estão ocultos no método showDialog () neste exemplo; não há necessidade de refatorá-lo duas vezes. O exemplo pretende mostrar um caso de uso isolado; se um padrão for necessário com diferentes configurações de diálogo em diferentes casos, faria sentido voltar e refatorar uma vez que o padrão seja estabelecido.
@ Thorbjorn: Eu pude ver um argumento para isso, quando você está respondendo a uma pergunta de domínio comercial cuja lógica pode não ser óbvia. Estou apenas apontando que há alguns casos em que é um exagero.
— #RedReady
Talvez isso seja preciso, mas eu diria que nos seus exemplos o problema não é que a função seja muito curta, mas que eles não agregam valor descritivo.
— 9123 keppla
De fato, a única maneira de garantir que:
É manter suas funções tão pequenas quanto possível. Ou, em outras palavras, extraia funções de suas funções até que você não possa mais extrair. Eu chamo isso de "Extrair até cair".
Para explicar isso: Uma função é um escopo com blocos de funcionalidade que se comunicam por variáveis. Uma classe também é um escopo com blocos de funcionalidade que se comunicam por variáveis. Portanto, uma função longa sempre pode ser substituída por uma ou mais classes com método pequeno.
Além disso, uma função que é grande o suficiente para permitir que você extraia outra função dela, está fazendo mais de uma coisa por definição . Portanto, se você pode extrair uma função de outra, deve extrair essa função.
Algumas pessoas temem que isso leve a uma proliferação de funções. Eles estão certos. Será. Isso é realmente uma coisa boa. É bom porque as funções têm nomes. Se você for cuidadoso ao escolher bons nomes, essas funções atuam como postagens de sinalização que direcionam outras pessoas através do seu código. De fato, funções bem nomeadas dentro de classes bem nomeadas dentro de namespaces bem nomeados são uma das melhores maneiras de garantir que seus leitores NÃO se percam.
Há muito mais sobre isso no episódio III do código limpo em cleancoders.com
Tio Bob! Ótimo vê-lo a bordo aqui. Como esperado, conselhos fantásticos. Eu nunca ouvi o termo "Extrair até cair" antes e certamente usará esse termo no escritório na segunda-feira;).
Você gostaria de elaborar seu ponto # 1? Explicar que pelo exemplo seria ótimo.
Uau, a maioria dessas respostas não ajuda muito.
Nenhuma função deve ser escrita cuja identidade seja sua definição . Ou seja, se o nome da função for simplesmente o bloco de código da função escrito em inglês, não a escreva como uma função.
Considere sua função conditionMete essa outra função addOne(perdoe-me pelo meu JavaScript enferrujado):
conditionMeté uma definição conceitual adequada; addOneé uma tautologia . conditionMeté bom porque você não sabe o que conditionMetimplica apenas dizendo "condição atendida", mas pode ver por que addOneé bobo se você ler em inglês:
(E pelo mesmo motivo, não escreva comentários para todas as linhas de código !)
Algumas das construções de linguagem mais complexas podem não ser familiares ou difíceis de ler, tornando esse truque útil.
Concordado, o que exatamente deveria ser transformado em uma função dependeria amplamente do idioma em que está sendo escrito. Uma função como addOne seria útil se você estivesse em um idioma como VHDL, onde na verdade está definindo o significado de adicionar um em um nível de teoria binária ou de número. Eu gostaria de pensar que nenhuma linguagem lá fora é tão enigmática que é difícil de ler, mesmo para programadores experientes (talvez brainf # ck), mas se for esse o caso, a mesma idéia permaneceria, já que a função é efetivamente uma tradução do inglês para esse idioma - portanto, o nome não é o mesmo que a definição.
— Rei Miyasaka 02/04
Estou falando de coisas função identidade da biblioteca padrão Haskell - Eu não acho que você pode obter mais "tautológico" então que :)
@missingno Bah, isso é trapaça: D
Se você nomear as funções pelo objetivo, e não pelo conteúdo, elas imediatamente deixarão de ser tolas. Portanto, seus exemplos podem ser, por exemplo, ser function nametoolong() { return name.size > 15; }ou. function successorIndex(x) { return x + 1; } O problema com suas funções é, na verdade, apenas que o nome delas é ruim.
Eu diria que, se você acha que a intenção de algum código pode ser aprimorada adicionando um comentário, em vez de adicionar esse comentário, extraia o código em seu próprio método. Não importa o tamanho do código.
Por exemplo, se o seu código for parecido com:
Ou, em outras palavras, não é sobre o tamanho do método, mas sobre o código de auto-documentação.
Meu estimado colega salienta que você também pode escrever if x == PIXEL_ON. Usar uma constante pode ser tão descritivo quanto usar um método e é um pouco mais breve.
Eu acho que isso é exatamente o que você quer fazer. No momento, essa função pode ter apenas uma ou duas linhas, mas com o tempo ela pode crescer. Também ter mais chamadas de função permite ler as chamadas de função e entender o que está acontecendo lá dentro. Isso torna seu código muito seco (não se repita), que é muito mais sustentável.
Então, o que há de errado em fatorá-lo mais tarde, quando você pode provar que precisa, em vez de agora, quando é apenas um peso morto?
As funções não crescem por conta própria. IMHO os exemplos são ruins. conditionMeté um nome muito genérico e não aceita argumentos, por isso testa algum estado? (x == xCondition) é, na maioria dos idiomas e situações, tão expressivo quanto 'xConditition'.
Sim, e é por isso que você deseja ter mais de 75% de sobrecarga no seu código? O revestimento simples, escrito como 3 linhas, é na verdade 300%.
Concordo com todos os outros posts que vi. Este é um bom estilo.
A sobrecarga de um método tão pequeno pode ser nula, pois o otimizador pode otimizar a ligação e alinhar o código. Um código simples como esse permite que o otimizador faça seu melhor trabalho.
O código deve ser escrito para maior clareza e simplicidade. Eu tento limitar um método a uma das duas funções: tomar decisões; ou realizando trabalho. Isso pode gerar métodos de uma linha. Quanto melhor eu estou fazendo isso, melhor eu acho o meu código.
EDIT: Uma nota sobre os nomes dos métodos. Use um nome de método que indique o que o método não faz e como faz. Acho verb_noun (_modifier) ​​é um bom esquema de nomenclatura. Isso fornece nomes como Find_Customer_ByName em vez de Select_Customer_Using_NameIdx. O segundo caso pode ficar incorreto quando o método é modificado. No primeiro caso, você pode trocar toda a implementação do banco de dados do Cliente.
+1 por mencionar inlining, na consideração principal em desempenho e funções curtas.
— perfil completo de Garet Claborn
A refatoração de uma linha de código em uma função parece excessiva. Pode haver casos excepcionais, como ver muitoooooongo / cumprimento de linhas ou expessões, mas eu não faria isso a menos que saiba que a função aumentará no futuro.
e seu primeiro exemplo sugere o uso de globais (que podem ou não falar de outros problemas no código), refatoraria ainda mais e tornaria essas duas variáveis ​​como parâmetros:
O conditionMetexemplo pode ser útil se a condição for longa e repetitiva, como:
Seu primeiro exemplo não sugere globais. Na verdade, é um método coeso em uma classe altamente coesa, na qual a maioria das variáveis ​​está no objeto.
Essa conditionMetfunção é apenas um ==operador detalhado . Provavelmente, isso não é útil.
Definitivamente não uso globals. @MeshMan, é exatamente de onde o exemplo é ... um método em uma classe.
— 27668 Mark
@ Mark Brown: @MeshMan: Ok, eu acho que o exemplo de código era demasiado vago para saber com certeza ...
Estou cheirando um conditionMet (x, relation condition) {aqui, onde você passa x, '==' e relação. Então você não precisa repetir o código para '<', '>', '<=', '! =' E assim por diante. Por outro lado - a maioria das linguagens tem essas construções incorporadas à medida que os operadores (condição x ==) fazem exatamente isso. Funções para instruções atômicas estão um passo longe demais.
Uma simples função de detecção de colisão:
Se você escreveu esse liner "simples" em seu código o tempo todo, pode cometer um erro. Além disso, seria realmente torturante escrever isso várias vezes.
Se estamos lidando com C aqui, nós poderíamos apenas abuso #define;)
Você pode até querer trocar isso para o mais lento, mas estouro prova de hypot se os seus tamanhos ou distâncias tornam-se muito grandes. Obviamente, isso é muito mais fácil de fazer uma vez.
Não, e isso raramente é um problema. Agora, se alguém sente que nenhuma função deve ter mais de uma linha de código (se pudesse ser assim tão simples), isso seria um problema e, de certa forma, preguiçoso, porque eles não estão pensando no que é apropriado.
Eu não contaria isso em linhas de código, mas em expressões. "(x == condition)" é atômico, portanto, eu o colocaria apenas em um método / função, se for usado várias vezes E puder ser alterado, como function isAdult () = { age >= 18; }mas certamente não isAtLeast18.
Eu diria que eles são muito curtos, mas esta é a minha opinião subjetiva.
Não há razão para criar uma função se ela for usada apenas uma ou duas vezes. Saltar para defs é péssimo. Especialmente com códigos VS e C ++ incrivelmente rápidos.
Visão geral da aula. Quando você tem milhares de pequenas funções, isso me deixa com raiva. Gosto de ver as definições das classes e ver rapidamente o que elas fazem, não como SetXToOne, SetYToVector3, MultiplyNumbers, + 100 setters / getters.
Na maioria dos projetos, esses auxiliares perdem peso após uma ou duas fases de refatoração e, em seguida, você "pesquisa todos" -> exclui para se livrar do código obsoleto, geralmente ~ 25% + dele.
Funções longas são ruins, mas funções menores que 3 linhas e executam apenas uma coisa são IMHO igualmente ruins.
Então, eu diria que apenas escreva função pequena se for:
Coisas que os desenvolvedores juniores podem perder (não sei)
Tem algum significado especial, por exemplo, especialização de modelo ou algo
Faz algum trabalho de isolamento - referências constantes, afeta parâmetros mutáveis, recupera membros privados
Aposto que o próximo desenvolvedor (sênior) terá coisas melhores a fazer do que lembrar de todas as suas funções SetXToOne. Então eles se transformarão em peso morto em breve, de qualquer maneira.
Eu não gosto do exemplo não. 1, por causa do i-ésimo nome genérico.
conditionMetnão parece ser genérico, significa uma condição específica? Gostar
Isso seria bom. É uma diferença semântica, enquanto
Talvez seja usado com frequência e possa ser sujeito a alterações posteriores:
está bem também. Ao usá-lo várias vezes, você só precisa mudar um único local, se for necessário - talvez chantilly seja possível amanhã.
Se você precisa passar uma função, é claro, passe o que quiser e escreva funções de aparência boba.
Uma última palavra: algumas funções parecem apropriadas, porque são escritas muito detalhadas:
Se você vê, é o mesmo que
Mantenha-o simples e não complique demais as coisas.
Sim, não há problema em ter uma função de código curto. No caso de métodos como "getters", "setters", "accesors" é muito comum, como as respostas anteriores mencionaram.
Às vezes, essas funções curtas de "accesors" são virtuais, porque quando substituídas nas subclasses, as funções terão mais código.
Se você deseja que sua função não seja tão curta, bem, em muitas funções, sejam elas globais ou métodos, eu geralmente uso uma variável "result" (estilo pascal) em vez de um retorno direto, é muito útil ao usar um depurador.
Curto demais nunca é um problema. Alguns motivos para funções curtas são:
Por exemplo, se você tem uma função, como um método definido, você pode afirmar que tem certeza de que os parâmetros são válidos. Essa verificação deve ser feita em qualquer lugar em que a variável estiver definida.
Você pode usar alguma declaração que acha que pode mudar no futuro. Por exemplo, agora você mostra um símbolo em uma coluna, mas depois isso pode mudar para outra coisa (ou até um sinal sonoro).
Você está usando, por exemplo, o padrão de fachada e a única coisa que uma função faz é passar exatamente a função para outra sem alterar os argumentos.
Quando você dá um nome a uma seção de código, é essencialmente para dar um nome a ela . Isso pode ser por qualquer um de vários motivos, mas o ponto crucial é se você pode atribuir um nome significativo ao seu programa. Nomes como "addOneToCounter" não adicionariam nada, mas conditionMet()sim.
Se você precisar de uma regra para descobrir se o snippet é muito curto ou muito longo, considere quanto tempo leva para encontrar um nome significativo para o snippet. Se você não puder dentro de um período de tempo razoável, o snippet não será um tamanho adequado.
Lembre-se: o código é escrito uma vez, mas é lido muitas vezes.
Não escreva código para o compilador. Escreva para futuros desenvolvedores que precisarão manter seu código.
Sim querido, a função pode ser cada vez menor e se é bom ou ruim, depende da linguagem / estrutura que você está usando.
Na minha opinião, eu trabalho principalmente em Front End Technologies, as Small Functions são usadas principalmente como funções auxiliares; você provavelmente as usará muito quando estiver trabalhando com filtros pequenos e usando a mesma lógica em seu aplicativo. Se seu aplicativo tiver muita lógica comum, haverá várias funções pequenas.
Porém, em um aplicativo em que você não possui lógica comum, não será obrigado a criar pequenas funções, mas poderá dividir seu código em segmentos em que fica fácil gerenciar e entender.
Em geral, dividir seu código enorme em pequenas funções é uma abordagem muito boa. Em estruturas e linguagens modernas, você será obrigado a fazê-lo, por exemplo,
é uma função anônima no ES 2017 JavaScript e Typescript
no código acima, você pode ver 3 declarações de funções e 2 chamadas de funções. Esta é uma simples chamada de serviço no Angular 4 com texto datilografado. Você pode pensar nisso como seus requisitos
Acima, são 3 funções anônimas na linguagem clojure
Portanto, sim, as FUNÇÕES podem ser menores, mas se é bom ou ruim, se você tem funções como:
Bem, não é uma boa prática fazê-lo, mas e se você estiver usando esta função em uma tag HTML, como fazemos no Angular Framework, se não houver suporte para Incremento ou Decremento em Modelos HTML Angulares, essa seria a solução para mim.
O exemplo acima é essencial ao reproduzir quando matrizes dentro de modelos HTML angulares. Então, às vezes, você terá que criar pequenas funções
— Anwaar Ulhaq
Não concordo com quase a resposta dada neste momento.
Recentemente eu encontrei um colega que escreve todas as turmas da seguinte forma:
Esse poderia ser um bom código em casos esporádicos, mas certamente não se você definir muitas classes com muitas e muitas propriedades. Não quero dizer, com isso, que métodos como os mencionados acima não sejam escritos. Mas, se você acabar escrevendo a maior parte do código como "invólucro" da lógica real, há algo errado.
Provavelmente, o programador perde a lógica geral, teme mudanças futuras e refatora o código.
A definição da interface é porque uma necessidade real; de fato, se você precisar definir e obter uma propriedade simples (sem muita lógica, o que reduz o membro), isso será possível. Mas, se a necessidade real pode ser analisada de uma maneira diferente, por que não definir uma interface mais intuitiva?
Para realmente responder à pergunta, é claro que não, e a razão é muito óbvia: o método / propriedade / o que mais deve ser definido para o que precisa. Mesmo uma função virtual vazia tem um motivo para existir.
Há um bom motivo para adicionar métodos de acesso / mutador, que você sugere em "medo de refatoração futura ...". Mas você também tem razão em não agregar valor ao código. Nem reduzindo o tamanho (local ou globalmente) ou aumentando a legibilidade. Na minha opinião, isso se refere ao design de linguagem ruim na convenção Java e JavaBeans. Essa é uma área (de muitas) em que o C # melhorou com êxito o idioma para tratar de ambas as preocupações com a sintaxe automática de propriedades . Como programador Java, concordo com seu estilo bruto para classes básicas do tipo estrutura.
As funções getter / setter são boas, mesmo que tudo o que façam agora seja ler ou gravar uma variável. Por uma questão prática, você pode imaginar um cenário em que mais tarde decida imprimir um valor na tela toda vez que o campo for alterado ou verificar se esse valor é válido ou não. (Talvez seja um denominador por uma fração, e você quer verificar para se certificar de que não é zero.)
getters e setters são terríveis, apesar de seu uso válido. Se for necessário usá-los, considero uma falha do idioma.
@Rei: por que uma classe externa precisaria verificar o valor dos denominadores? Isso deve ser feito pela função divide () da classe Fraction ou a classe Fraction deve fornecer um isDivisible () para verificar se há denominadores nulos. Os getters / setters necessários geralmente são um cheiro de código que sua classe tem alto acoplamento e / ou baixa coesão (ou seja, ruim).
@Lie What? Eu nunca disse que uma classe externa deveria ser usada para verificar denominadores. Estou dizendo que uma classe externa pode definir um denominador para 0 por acidente. O objetivo de realizar verificações de validade nos levantadores é incentivar quaisquer bugs no código do consumidor a serem encontrados anteriormente. Fornecer uma função isDivisible () que pode ou não ser chamada também não funciona para esse fim. E como exatamente a necessidade de getters / setters indica que há um acoplamento alto?