Funções de uma linha que são chamadas apenas uma vez

116

Considere uma função sem parâmetros ( edição: não necessariamente) que executa uma única linha de código, e é chamada apenas uma vez no programa (embora não seja impossível que seja necessário novamente em o futuro).

Ele poderia realizar uma consulta, verificar alguns valores, fazer algo envolvendo regex ... qualquer coisa obscura ou "hacky".

A razão por trás disso seria evitar avaliações dificilmente legíveis:

if (getCondition()) {
    // do stuff
}

onde getCondition() é a função de uma linha.

Minha pergunta é simples: essa é uma boa prática? Parece tudo bem para mim, mas eu não sei sobre o longo prazo ...

    
por vemv 12.09.2011 / 16:34
fonte

11 respostas

237

Depende dessa linha. Se a linha for legível e concisa, a função pode não ser necessária. Exemplo simplista:

void printNewLine() {
  System.out.println();
}

OTOH, se a função der um bom nome a uma linha de código contendo, e. uma expressão complexa, difícil de ler, é perfeitamente justificada (para mim). Exemplo criado (dividido em várias linhas para facilitar a leitura aqui):

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
    
por 12.09.2011 / 16:36
fonte
66

Sim, isso pode ser usado para satisfazer as práticas recomendadas. Por exemplo, é melhor ter uma função claramente nomeada para fazer algum trabalho, mesmo que seja apenas uma linha longa, do que ter essa linha de código dentro de uma função maior e precisar de um comentário de uma linha explicando o que ela faz. Além disso, linhas de código vizinhas devem executar tarefas no mesmo nível de abstração. Um contra-exemplo seria algo como

startIgnition();
petrolFlag |= 0x006A;
engageChoke();

Neste caso, é definitivamente melhor mover a linha do meio para uma função de nome sensato.

    
por 12.09.2011 / 16:39
fonte
37

Eu acho que em muitos casos essa função é um bom estilo, mas você pode considerar uma variável booleana local como alternativa nos casos em que você não precisa usar essa condição em algum outro lugar, por exemplo:

bool someConditionSatisfied = [complex expression];

Isso dará uma dica para o leitor de código e economizará na introdução de uma nova função.

    
por 12.09.2011 / 17:15
fonte
23

Além de a resposta de Peter , se essa condição precisar ser atualizada em algum momento no futuro, encapsulando a maneira como você sugere que você teria apenas um único ponto de edição.

Seguindo o exemplo de Peter, se isso

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

torna-se isso

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isMutant() 
        && (taxPayer.getNumberOfThumbs() > 2 
        || (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}

Você faz uma única edição e é atualizada universalmente. Manutenção sábia, isso é uma vantagem.

O desempenho, a maioria dos compiladores de otimização removerá a chamada de função e o bloco de código pequeno de qualquer maneira. A otimização de algo assim pode reduzir o tamanho do bloco (excluindo as instruções necessárias para a chamada de função, retorno, etc.), por isso é normalmente seguro fazê-lo mesmo em condições que poderiam impedir o inlining.

    
por 12.09.2011 / 17:13
fonte
5

Além da legibilidade (ou complemento dele), isso permite escrever funções no nível apropriado de abstração.

    
por 12.09.2011 / 20:00
fonte
4

Depende. Às vezes é melhor encapsular uma expressão em uma função / método, mesmo que seja apenas uma linha. Se é complicado ler ou você precisa em vários lugares, então eu considero uma boa prática. A longo prazo, é mais fácil manter, pois você introduziu um ponto único de mudança e melhor legibilidade .

No entanto, às vezes é algo que você não precisa. Quando a expressão é fácil de ler e / ou aparece em apenas um lugar, não a envolva.

    
por 12.09.2011 / 16:38
fonte
3

Acho que se você tiver apenas alguns deles, tudo bem, mas o problema surge quando há muitos deles em seu código. E quando o compilador é executado ou o interpetor (dependendo do idioma que você usa) Ele vai para essa função na memória. Então vamos dizer que você tem 3 deles Eu não acho que o computador vai notar, mas se você começar a ter 100 dessas coisinhas, então o sistema tem que registrar funções na memória que são chamadas apenas uma vez e depois não são destruídas.

    
por 13.09.2011 / 07:36
fonte
3

Eu fiz exatamente isso recentemente em um aplicativo que refatorei para tornar explícito o significado real do código sem comentários:

protected void PaymentButton_Click(object sender, EventArgs e)
    Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;

    CheckInputs();

    if(HaveError())
        return;

    ...
}
    
por 12.09.2011 / 20:33
fonte
0

Mover essa linha para um método bem nomeado facilita a leitura do seu código. Muitos outros já mencionaram isso ("código de auto-documentação"). A outra vantagem de movê-lo para um método é que ele facilita o teste de unidade. Quando ele é isolado em seu próprio método e testado em unidade, você pode ter certeza de que, se / quando um bug for encontrado, ele não estará nesse método.

    
por 16.09.2011 / 06:15
fonte
0

Já existem muitas respostas boas, mas há um caso especial que vale a pena mencionar .

Se sua instrução de uma linha precisar de um comentário e você conseguir identificar claramente (o que significa: nome) sua finalidade, considere a extração de uma função enquanto aprimora o comentário no documento da API. Desta forma, você torna a chamada de função mais fácil e mais rápida de entender.

Curiosamente, o mesmo pode ser feito se não houver nada a ser feito atualmente, mas um comentário lembrando as expansões necessárias (no futuro muito próximo 1) ), portanto,

def sophisticatedHello():
    # todo set up
    say("hello")
    # todo tear down

também poderia mudar para isso

def sophisticatedHello():
    setUp()
    say("hello")
    tearDown()

1) você deve ter certeza disso (veja o princípio do YAGNI )

    
por 09.01.2015 / 17:08
fonte
0

Se o idioma for compatível, normalmente uso funções anônimas rotuladas para fazer isso.

someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
    #do stuff...

IMHO é um bom compromisso porque dá a você o benefício da legibilidade de não ter a expressão complicada sobrecarregando a condição if , evitando bagunçar o namespace global / package com pequenos rótulos descartáveis. Ele tem o benefício adicional de que a função "definição" está exatamente onde está sendo usada, facilitando a modificação e a leitura da definição.

Não precisa ser apenas funções de predicado. Eu gosto de envolver placas de caldeira repetidas em pequenas funções como esta também (funciona muito bem para geração de listas pythonic sem confundir a sintaxe de colchetes). Por exemplo, o seguinte exemplo simplificado ao trabalhar com o PIL em python

#goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L')) 
arr_list = [im_2_arr(image) for image in image_list]
    
por 13.09.2011 / 02:43
fonte

Tags