Passando funções para outras funções como parâmetros, má prática?

39

Estamos no processo de alterar a forma como o nosso aplicativo AS3 fala com o nosso back-end e estamos no processo de implementar um sistema REST para substituir o antigo.

Infelizmente, o desenvolvedor que começou o trabalho agora está em licença médica prolongada e foi entregue a mim. Eu tenho trabalhado com isso pela última semana ou mais agora e eu entendo o sistema, mas tem uma coisa que está me preocupando. Parece haver muita passagem de funções em funções. Por exemplo, nossa classe que faz a chamada para nossos servidores recebe uma função que irá chamar e passar um objeto para quando o processo estiver completo e os erros tiverem sido manipulados, etc.

Está me dando aquela "sensação ruim", onde eu sinto que é uma prática horrível e posso pensar em algumas razões, mas eu quero alguma confirmação antes de propor um retrabalho para o sistema. Eu queria saber se alguém teve alguma experiência com este possível problema?

    
por Elliot Blackburn 20.08.2014 / 11:39
fonte

5 respostas

83

Não é um problema.

É uma técnica conhecida. Estas são funções de maior ordem (funções que tomam funções como parâmetros).

Esse tipo de função também é um bloco de construção básico na programação funcional e é usado extensivamente em linguagens funcionais como < Hask = Haskell .

Tais funções não são ruins ou boas - se você nunca encontrou a noção e a técnica, elas podem ser difíceis de entender no início, mas elas podem ser muito poderosas e são uma boa ferramenta para se ter em seu cinto de ferramentas.

    
por 20.08.2014 / 11:48
fonte
30

Eles não são usados apenas para programação funcional. Eles também podem ser conhecidos como callbacks :

A callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. The invocation may be immediate as in a synchronous callback or it might happen at later time, as in an asynchronous callback.

Pense no código assíncrono por um segundo. Você passa uma função que, por exemplo, envia dados para o usuário. Somente quando o código é concluído você invoca essa função com o resultado da resposta, que a função usa para enviar os dados de volta ao usuário. É uma mudança de mentalidade.

Eu escrevi uma biblioteca que recupera dados do Torrent de sua seedbox. Você está usando um loop de eventos sem bloqueio para executar essa biblioteca e obter dados, depois retorná-los ao usuário (digamos, em um contexto de websocket). Imagine que você tenha 5 pessoas conectadas neste evento, e uma das solicitações para que os dados de torrent de alguém parem. Isso irá bloquear todo o loop. Então você precisa pensar de forma assíncrona e usar callbacks - o loop continua rodando e o "dando os dados de volta para o usuário" só é executado quando a função termina a execução, então não há como esperar por ela. Fogo e esqueça.

    
por 20.08.2014 / 17:45
fonte
11

Isso não é uma coisa ruim. Na verdade, é uma coisa muito boa.

A passagem de funções para funções é tão importante para a programação que inventamos funções lambda como abreviação. Por exemplo, pode-se usar lambdas com algoritmos C ++ para escrever de forma muito compacta ainda código expressivo que permite um algoritmo genérico a capacidade de usar variáveis locais e outro estado para fazer coisas como pesquisar e ordenar.

As bibliotecas orientadas a objetos também podem ter retornos de chamada , que são essencialmente interfaces que especificam um pequeno número de funções ( idealmente um, mas nem sempre). Pode-se então criar uma classe simples que implemente essa interface e passe um objeto dessa classe para uma função. Essa é uma pedra angular da programação orientada a eventos , onde o código no nível do framework (talvez até mesmo em outro segmento) precisa chamar em um objeto para mudar de estado em resposta a uma ação do usuário. A interface ActionListener do Java é um bom exemplo de isso.

Tecnicamente, um functor C ++ também é um tipo de objeto de retorno de chamada que aproveita o açúcar sintático, operator()() , para fazer a mesma coisa.

Finalmente, há ponteiros de função no estilo C que só devem ser usados em C. Não vou entrar em detalhes, apenas menciono-os para completude. As outras abstrações mencionadas acima são muito superiores e devem ser usadas em idiomas que as possuam.

Outros mencionaram a programação funcional e como funções de passagem são muito naturais nessas linguagens. Lambdas e callbacks são a forma como as linguagens procedurais e OOP imitam isso, e são muito poderosas e úteis.

    
por 21.08.2014 / 06:51
fonte
6

Como já foi dito, não é uma prática ruim. É apenas uma maneira de dissociar e separar a responsabilidade. Por exemplo, em OOP você faria algo assim:

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

O método genérico é delegar uma tarefa específica - sobre a qual nada se sabe - para outro objeto que implemente uma interface. O método genérico conhece apenas essa interface. No seu caso, essa interface seria uma função a ser chamada.

    
por 20.08.2014 / 11:55
fonte
3

Em geral, não há nada de errado em passar funções para outras funções. Se você estiver fazendo chamadas assíncronas e quiser fazer alguma coisa com o resultado, precisará de algum mecanismo de retorno de chamada.

Existem algumas desvantagens potenciais de retornos de chamada simples:

  • Fazer uma série de chamadas pode exigir o aninhamento profundo de retornos de chamada.
  • O tratamento de erros pode precisar de repetição para cada chamada em uma sequência de chamadas.
  • Coordenar várias chamadas é complicado, como fazer várias chamadas ao mesmo tempo e, em seguida, fazendo algo uma vez que estão todos terminados.
  • Não há maneira geral de cancelar um conjunto de chamadas.

Com webservices simples, a maneira como você está fazendo funciona bem, mas torna-se complicado se você precisar de um sequenciamento mais complexo de chamadas. Existem algumas alternativas embora. Com o JavaScript, por exemplo, houve uma mudança no uso de promessas ( O que é ótimo sobre promessas de javascript .

Eles ainda envolvem a passagem de funções para outras funções, mas as chamadas assíncronas retornam um valor que recebe um retorno de chamada em vez de receber um retorno de chamada diretamente. Isso dá mais flexibilidade para compor essas chamadas juntas. Algo como isso pode ser implementado com bastante facilidade no ActionScript.

    
por 20.08.2014 / 22:04
fonte