Uma função pode ser muito curta?

125

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 na minha aplicação para manter essa lógica. Um efeito colateral é que às vezes acabo com uma ou duas funções de linha como:

function conditionMet(){
   return x == condition;
}

OR

function runCallback(callback){
   if($.isFunction(callback))
     callback();
}

Isso é preguiçoso ou uma prática ruim? Eu só peço porque isso resulta em um número maior de chamadas de função para pequenos pedaços de lógica.

    
por Mark Brown 01.04.2011 / 21:34
fonte

19 respostas

166

Hehe, oh Sr. Brown, se eu pudesse persuadir todos os desenvolvedores que encontrei para manter suas funções tão pequenas quanto isso, acredite, o mundo dos softwares seria um lugar melhor!

1) A legibilidade do seu código aumenta dez vezes.

2) É tão fácil descobrir o processo do seu código por causa da legibilidade.

3) SECA - Não se Repita - Você está de acordo com isso muito bem!

4) Testável. Funções minúsculas 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 "função de salto" em termos de desempenho. As compilações "Release" e as otimizações do compilador cuidam disso muito bem, e o desempenho é 99% do tempo em algum outro lugar no projeto de sistemas.

Isso é preguiçoso? - Muito pelo contrário!

Isso é uma prática ruim? - Absolutamente não. É melhor puxar essa maneira de fazer métodos do que as bolas de alcatrão ou "Objetos Divinos" que são tão comuns.

Mantenha o bom trabalho do meu colega artesão;)

    
por 01.04.2011 / 21:27
fonte
63

Eu diria que um método refatorado é muito curto se:

  • Duplica uma operação primitiva, para nenhum outro propósito que não seja um método:

Ex:

boolean isNotValue() {
   return !isValue();
}

ou ...

  • O código é usado apenas uma vez e sua intenção é fácil de entender em um piscar de olhos.

Ex:

void showDialog() {
    Dialog singleUseDialog = new ModalDialog();
    configureDialog(singleUseDialog);
    singleUseDialog.show();
}

void configureDialog(Dialog singleUseDialog) {
    singleUseDialog.setDimensions(400, 300);
}

Este poderia ser um padrão válido, mas eu apenas incluiria o método configureDialog (), neste exemplo, a menos que eu pretendesse substituí-lo ou reutilizá-lo em outro lugar.

    
por 02.04.2011 / 03:49
fonte
59

Uma função pode ser muito curta? Em geral não.

Na verdade, a única maneira de garantir isso:

  1. Você encontrou todas as classes em seu design
  2. Suas funções estão fazendo apenas uma coisa.

É manter suas funções tão pequenas quanto possível. Ou, em outras palavras, extraia funções de suas funções até não conseguir mais extrair. Eu chamo isso de "Extrair até cair".

Para explicar isso: Uma função é um escopo com partes de funcionalidade que se comunicam por variáveis. Uma classe também é um escopo com partes de funcionalidade que se comunicam por variáveis. Portanto, uma função longa pode sempre ser substituída por uma ou mais classes com um 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 . Então, se você puder extrair uma função de outra, você deve extrair essa função.

Algumas pessoas se preocupam 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 funcionarão como postagens de sinal que direcionam outras pessoas por meio de seu código. Na verdade, funções bem nomeadas dentro de classes bem nomeadas dentro de namespaces bem nomeados são uma das melhores maneiras de se certificar de que seus leitores não se perdem.

Há muito mais sobre isso no episódio III do Clean Code em cleancoders.com

    
por 02.04.2011 / 01:25
fonte
51

Nossa, a maioria dessas respostas não ajuda muito.

Nenhuma função deve ser escrita, cuja identidade é a 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 o escreva como uma função.

Considere sua função conditionMet e esta outra função, addOne (perdoe-me pelo meu JavaScript enferrujado):

function conditionMet() { return x == condition; }

function addOne(x) { return x + 1; }

conditionMet é uma definição conceitual adequada; addOne é uma tautologia . conditionMet é bom porque você não sabe o que conditionMet implica apenas dizendo "condição satisfeita", mas você pode ver porque addOne é bobo se você lê em inglês:

"For the condition to be met is for x to equal condition" <-- explanatory! meaningful! useful!

"To add one to x is to take x and add one." <-- wtf!

Pelo amor de qualquer coisa que ainda possa ser santa, por favor, não escreva funções tautológicas!

(E pela mesma razão, não escreva comentários para cada linha de código !)

    
por 12.04.2017 / 09:31
fonte
14

Eu diria que se você acha que a intenção de algum código pode ser melhorada adicionando um comentário, em vez de adicionar esse comentário, extraia o código em seu próprio método. Não importa quão pequeno seja o código.

Por exemplo, se o seu código fosse parecido com:

if x == 1 { ... } // is pixel on?

faça com que pareça com isso:

if pixelOn() { ... }

com

function pixelOn() { return x == 1; }

Ou, em outras palavras, não é sobre o tamanho do método, mas sobre o código de autodocumentação.

    
por 07.02.2013 / 21:46
fonte
7

Eu acho que isso é exatamente o que você quer fazer. Agora, essa função pode ser apenas uma ou duas linhas, mas com o tempo ela pode crescer. Também ter mais chamadas de função permite que você leia as chamadas de função e entenda o que está acontecendo lá dentro. Isso torna seu código muito DRY (não se repita) , que é muito mais sustentável.

    
por 01.04.2011 / 22:07
fonte
5

Concordo com todas as outras postagens que vi. Isso é bom estilo.

A sobrecarga de um método tão pequeno pode ser nula, pois o otimizador pode otimizar a chamada e embutir o código. Código simples como este permite que o otimizador faça o melhor trabalho.

O código deve ser escrito para maior clareza e simplicidade. Eu tento limitar um método a um dos dois papéis: tomar decisões; ou executar o trabalho. Isso pode gerar um método de linha. Quanto melhor eu fizer isso, melhor encontrarei meu código.

Código como este tende a ter alta coesão e baixo acoplamento, o que é uma boa prática de codificação.

EDIT: Uma nota sobre nomes de métodos. Use um nome de método que indique o que o método não faz como faz. Eu acho que 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 é propenso a se tornar incorreto quando o método é modificado. No primeiro caso, você pode trocar toda a implementação do banco de dados do Cliente.

    
por 09.06.2011 / 02:19
fonte
4

Refatorar uma linha de código em uma função parece excessivo. Pode haver casos excepcionais, como veroooooo / comples linhas ou expessões, mas eu não faria isso a menos que eu conheça que a função crescerá no futuro.

e seu primeiro exemplo sugere o uso de globais (que podem ou não falar de outras questões no código), eu o refatorei mais, e faço essas duas variáveis como parâmetros:

function conditionMet(x, condition){
   return x == condition;
}
....
conditionMet(1,(3-2));
conditionMet("abc","abc");

O conditionMet example pode ser útil se a condição for longa e repetitiva, como:

function conditionMet(x, someObject){
   return x == ((someObject.valA + someObject.valB - 15.4) / /*...whole bunch of other stuff...*/);
}
    
por 01.04.2011 / 21:43
fonte
3

Considere isso:

Uma função simples de detecção de colisão:

bool collide(OBJ a, OBJ b)
{
    return(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) <= pow(a.radius + b.radius, 2));
}

Se você escreveu aquele "simples" um forro em seu código o tempo todo, você pode eventualmente cometer um erro. Além disso, seria muito torturante escrever isso repetidamente.

    
por 02.04.2011 / 10:35
fonte
2

Não, e isso raramente é um problema. Agora, se alguém achar que nenhuma função deve ser mais longa do que uma linha de código (se é que pode ser assim tão simples), isso seria um problema e, de certa forma, preguiçoso porque eles não estão pensando sobre o que é apropriado.

    
por 01.04.2011 / 22:17
fonte
2

Eu diria que eles são muito curtos, mas essa é a minha opinião subjetiva.

Porque:

  • Não há razão para criar uma função se ela for usada apenas uma ou duas vezes. Saltando para defs chupar. Especialmente com o incrivelmente rápido código VS e C ++.
  • Visão geral da turma. Quando você tem milhares de pequenas funções, isso me deixa com raiva. Eu gosto de ver as definições de classe e ver rapidamente o que ele faz, não como SetXToOne, SetYToVector3, MultiplyNumbers, + 100 setters / getters.
  • Na maioria dos projetos, esses ajudantes se tornam um peso morto depois de uma ou duas fases de refatoração, e então você "pesquisa todos" - > delete para se livrar do código obsoleto, geralmente ~ 25% disso.

Funções que são longas são ruins, mas funções que são menores que 3 linhas e executam apenas 1 coisa são igualmente ruins IMHO.

Então, eu diria que só escrevesse uma pequena função se fosse:

  • Mais de 3 linhas de código
  • Coisas que os desenvolvedores juniores podem perder (não sabem)
  • Faz validação extra
  • é usado ou será usado pelo menos 3x
  • Simplifica a interface usada com frequência
  • Não se tornará um peso morto durante a próxima refatoração
  • Tem algum significado especial, por exemplo, especialização em modelo ou algo
  • Faz algum trabalho de isolamento - faz referências a const, afeta parâmetros mutáveis, recupera membros privados

Aposto que o próximo desenvolvedor (senior) terá coisas melhores a fazer do que lembrar de todas as suas funções SetXToOne. Então eles vão se transformar em peso morto muito em breve de qualquer forma.

    
por 09.06.2011 / 15:49
fonte
1

Eu não gosto do exemplo não. 1, por causa do nome genérico.

conditionMet não parece ser genérico, então é uma condição específica? Como

isAdult () = { 
  age >= 18 
}

Isso seria ótimo. É uma diferença semântica, enquanto

isAtLeast18 () { age >= 18; } 

não seria bom para mim.

Talvez seja usado com frequência e possa estar sujeito a alterações posteriores:

getPreferredIcecream () { return List ("banana", "choclate", "vanilla", "walnut") }

está bem também. Usá-lo várias vezes, você só precisa mudar um único lugar, se você precisar - talvez chantilly seja possível amanhã.

isXYZ (Foo foo) { foo.x > 15 && foo.y < foo.x * 2 }

não é atômico e deve oferecer uma boa oportunidade de teste.

Se você precisar passar por uma função, é claro, passe o que quiser e escreva outras funções que pareçam bobas.

Mas, em geral, vejo muito mais funções, que são muito longas, do que funções que são muito curtas.

Uma última palavra: algumas funções parecem adequadas, porque são escritas de forma muito detalhada:

function lessThan (a, b) {
  if (a < b) return true else return false; 
}

Se você ver, é o mesmo que

return (a < b); 

você não terá problemas com

localLessThan = (a < b); 

em vez de

localLessThan = lessThan (a, b); 
    
por 09.06.2011 / 03:51
fonte
0

Não há código como nenhum código!

Mantenha as coisas simples e não complique as coisas.

Não é ser preguiçoso, está fazendo o seu trabalho!

    
por 02.04.2011 / 07:06
fonte
0

Sim, é ok ter uma função de código curto. No caso de métodos como "getters", "setters", "accesors" é muito comum, como as respostas anteriores mencionadas.

Às vezes, essas funções de "accesors" curtas são virtuais, porque quando sobrescritas em subclasses, as funções terão mais código.

Se você quiser que a função não seja tão curta, bem, em muitas funções, globais ou métodos, geralmente uso uma variável "resultado" (estilo pascal) em vez de um retorno direto, é muito útil quando se usa um depurador.

function int CalculateSomething() {
  int Result = -1;

   // more code, maybe, maybe not

  return Result;
}
    
por 02.04.2011 / 21:40
fonte
0

Demasiado curto, nunca é um problema. Algumas razões para funções curtas são:

Reutilização

Por exemplo Se você tiver uma função, como um método set, pode declarar para ter certeza de que os parâmetros são válidos. Essa verificação deve ser feita em todos os locais em que a variável está definida.

Manutenção

Você pode usar alguma declaração que, no futuro, possa mudar. Por exemplo. Agora você mostra um símbolo em uma coluna, mas depois isso pode mudar para outra coisa (ou mesmo um bipe).

Uniformidade

Você está usando, por exemplo, o padrão de fachada e a única coisa que uma função faz é exatamente passar a função para outra sem alterar os argumentos.

    
por 14.08.2012 / 16:47
fonte
0

Quando você dá um nome à seção de código, é essencialmente para dar um nome . Isso pode ser por qualquer um dos vários motivos, mas o ponto crucial é se você pode dar um nome significativo que adicione ao seu programa. Nomes como "addOneToCounter" não adicionariam nada, mas conditionMet() seria.

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 em um período de tempo razoável, o snippet não terá um tamanho adequado.

    
por 14.08.2012 / 17:33
fonte
0

Não, mas pode ser muito conciso.

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 terão que manter seu código.

    
por 08.02.2013 / 01:55
fonte
-1

Sim, querida, a função pode ser menor ou menor e boa ou ruim depende do idioma / estrutura que você está usando.

Na minha opinião, eu trabalho principalmente em tecnologias de front-end, as Small Functions são usadas principalmente como funções auxiliares e você poderá usá-las muito ao trabalhar com filtros pequenos e usar a mesma lógica em seu aplicativo. Se a sua aplicação tiver muita lógica comum, haverá muitas funções pequenas.

Mas, em um aplicativo em que você não tem uma lógica comum, não será obrigado a fazer pequenas funções, mas poderá dividir seu código em segmentos, onde fica mais fácil para você gerenciar e entender.

Em geral, quebrar seu código enorme em uma função pequena é uma abordagem muito boa. Em estruturas e linguagens modernas, você será obrigado a fazê-lo, por exemplo,

data => initScroll(data)

é uma função anônima no ES 2017 JavaScript e no Typescript

getMarketSegments() {
 this.marketService.getAllSegments(this.project.id)
  .subscribe(data => this.segments = data, error => console.log(error.toString()));
}

no código acima, você pode ver 3 declarações de função e 2 chamadas de função. Esta é uma chamada de serviço simples em Angular 4 com texto digitado. Você pode pensar nisso como suas necessidades

([] 0)
([x] 1)
([x y] 2)

Os itens acima são 3 funções anônimas na linguagem clojure

(def hello (fn [] "Hello world"))

O acima é uma declaração funcional em clojure

Então, sim, as FUNÇÕES podem ser menores, mas boas ou ruins, se você tiver funções como:

incrementNumber(numb) { return ++numb; }

Bem, não é uma boa prática fazer isso, mas e se você estiver usando essa função em uma tag HTML, como fizemos no Angular Framework, se não houver suporte para Increment ou Decrement em Angular HTML Templates, isso seria a solução para mim.

Vamos dar outro exemplo

insertInArray(array, newKey) {
 if (!array.includes(newKey)) {
   array.push(newKey);
 }
}

O exemplo acima é indispensável ao reproduzir em arrays dentro de modelos HTML angulares. Então, às vezes você terá que criar pequenas funções

    
por 17.07.2017 / 10:54
fonte
-2

Eu não concordo com quase a resposta dada neste momento.

Recentemente, encontrei um colega que escreve todos os membros da turma como:

 void setProperty(int value){ mValue=value; }
 int getProperty() const { return (mValue); }

Este poderia ser um bom código em casos esporádicos, mas certamente não se você definir muitas classes com muitas e muitas propriedades. Eu não quero dizer, com isso, que métodos como os acima não serão 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 não percebe 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ê precisa definir e obter uma propriedade simples (sem muita lógica, o que torna o membro mais curto), isso deve ser possível. Mas, se a necessidade real pudesse 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 / whatelse deve ser definido para o que é necessário. Até mesmo uma função virtual vazia tem um motivo para existir.

    
por 02.04.2011 / 00:15
fonte