Qual é o benefício de uma função sem parâmetros que apenas chama outra função


21

Um tutorial (para Javascript) que estou fazendo sugere que escrevamos uma função como esta:

function sayHello() {
   //Some comments explaining the next line
   window.alert("Hello");
}

Além da ofuscação, existem benefícios em escrever algo assim na vida real? Se sim, quais são os benefícios?


14
Provavelmente, está apenas tentando mostrar como definir suas próprias funções.
Doval

4
Eu nunca escreveria um código de modo a ofuscar - o código é para o programador ler facilmente, e não o contrário. Use um ofuscador para ofuscação.
#

32
O benefício é que envolve uma função com parâmetros. Dessa forma, se você quiser tornar seu aplicativo mais tarde, basta alterar "Olá" para "Ola" em um só lugar.
Pieter B

9
@PieterB, na verdade, "Hola"
rev

29
@ PieterB - E se você descobrir que digitou incorretamente "Hola", precisará corrigi-lo em um só lugar.
Daniel R Hicks

Respostas:


60

Perdoe minha memória se eu tiver esse incorreto ... Javascript não é minha linguagem de implementação preferida.

Há várias razões pelas quais alguém deseja que uma função no arg encerre outra chamada de função. Embora a simples chamada para window.alert("Hello");seja algo que você possa imaginar, basta ligar diretamente em vez de sayHello().

Mas e se houver mais? Você tem uma dúzia de lugares para onde deseja ligar sayHello()e escrever window.alert("Hello");. Agora você quer que faça um window.alert("Hello, it is now " + new Date()). Se você atendeu a todas essas chamadas, sayHello()você o mudou em um só lugar. Se não, você muda em uma dúzia de lugares. Isso toca em Não se repita . Você faz isso porque não deseja fazer isso uma dúzia de vezes no futuro.

Eu trabalhei com uma biblioteca i18n / l10n no passado que usava funções para fazer a localização do texto no lado do cliente. Considere a sayHello()função Você pode imprimir holaquando o usuário estiver localizado em um idioma espanhol. Isso pode ser algo como:

function sayHello() {
  var language = window.navigator.userLanguage || window.navigator.language;
  if(language === 'es') { window.alert('Hola'); }
   else { window.alert("Hello"); }
}

Porém, não foi assim que a biblioteca funcionou. Em vez disso, tinha um conjunto de arquivos parecido com:

# English file
greeting = hello
# Spanish file
greeting = hola

E a biblioteca detectaria a configuração do idioma do navegador e criaria funções dinâmicas com a localização apropriada como o valor de retorno para uma chamada de função sem argumento com base no arquivo de localização apropriado.

Não sou um codificador Javascript suficiente para dizer se isso é bom ou ruim ... apenas era e pode ser visto como uma abordagem possível.

O ponto é que, agrupar a chamada para outra função em uma função própria geralmente é bastante útil e ajuda na modularização do aplicativo e também pode resultar em código de leitura mais fácil.

Tudo isso à parte, você está trabalhando em um tutorial. É necessário introduzir as coisas da maneira mais simples possível no início. A introdução de chamadas de função no estilo varargs desde o início pode resultar em um código muito confuso para uma pessoa que não está familiarizada com a codificação em geral. É muito mais fácil passar de nenhum argumento, para argumentos, para o estilo varargs - com cada construção nos exemplos e no entendimento anteriores.


3
window.alerttambém é freqüentemente usado como espaço reservado durante o desenvolvimento até que um bom modal / pop-up possa ser projetado / implementado; portanto, mesmo que o problema de idioma, ele possa ser trocado com muito mais facilidade. Ou, se você já tiver uma, uma atualização de design daqui a alguns anos poderá exigir alterações semelhantes.
Izkata

34

Eu acho que às vezes é útil para ocultar a implementação.

 function sayHello() {
  window.alert("Hello");
 }

E isso lhe dá a flexibilidade de alterá-lo mais tarde

 function sayHello() {
  console.log("Hello");
 }

19

Além da ofuscação, existem benefícios em escrever algo assim na vida real? Se sim, quais são os benefícios?

  • centralização: embora a implementação tenha uma única linha, se for uma linha que muda frequentemente, é provável que você prefira alterá-la em um único local, do que onde quer que diga Olá seja chamado.

  • minimização / ocultação de dependências: seu código do cliente não precisa mais saber que existe um objeto de janela e você pode até alterar toda a implementação sem afetar o código do cliente

  • cumprimento do contrato: o código do cliente pode esperar um módulo que tenha uma função sayHello. Nesse caso, mesmo que trivial, a função deve estar lá.

  • consistência dos níveis de abstração: se o código do cliente usa operações de alto nível, é de seu interesse escrever o código do cliente em termos de 'sayHello, sayBye' e algumas outras funções 'sayXXX', em vez de objetos de janela. De fato, no código do cliente, você pode nem querer saber que existe um objeto de 'janela'.


Eu gosto desta resposta. Geralmente, o design é a principal razão para essas funções, especialmente o design OO, onde você deseja que objetos responsáveis ​​pelo comportamento executem funções de outros objetos com comportamento específico. Imaginando seu carro, mais particularmente você trocando de marcha. Você muda a sua marcha "dizendo" à alavanca de câmbio. Por sua vez, encaminha esta chamada para sua caixa de velocidades. Além disso, ele primeiro verifica se você pode mudar da sua posição atual.
Eric

3
Os outros motivos são bons, mas +1 para mencionar os níveis de abstração. O uso de abstrações claras e bons nomes de funções pode facilitar a leitura e a manutenção de códigos antigos. No ponto em que sayHello () é chamado, você não está necessariamente preocupado com a forma como é implementado, apenas quer entender qual é a intenção dessa linha de código. E um nome de função como sayHello () torna isso óbvio.
Kkrambo #

Também +1 para mencionar níveis de abstração: pode acontecer que a implementação de uma função em um nível de abstração consista em uma chamada para outra função de um nível de abstração mais baixo. E daí?
Giorgio

7

Surpreendente que nenhuma outra pessoa tenha mencionado testes.

A linha "embrulhada" específica que você escolheu window.alert('hello')é realmente um exemplo perfeito disso. Qualquer coisa que envolva o windowobjeto é muito, muito difícil de testar. Multiplique isso por 1000 vezes no seu aplicativo e garanto que os desenvolvedores acabarão desistindo dos testes. Por outro lado, é muito fácil ignorar a sayHellofunção com um espião e testar como foi chamada.

Um exemplo mais prático - porque, na verdade, quem realmente usa window.alert(...)no código de produção? - está verificando o relógio do sistema. Então, por exemplo, isso seria agrupado DateTime.Nowem .NET, time(...)C / C ++ ou System.currentTimeMillis()Java. Você realmente deseja agrupar aqueles em uma dependência que possa injetar, porque eles não são apenas (quase) impossíveis de zombar / falsificar, eles são somente leitura e não determinísticos . Qualquer teste que cubra uma função ou método que faça uso direto da função de relógio do sistema é extremamente provável de sofrer falhas intermitentes e / ou aleatórias.

O wrapper real é uma função de 1 linha - return DateTime.Now- mas é tudo o que você precisa para pegar um objeto mal testado e não testável e torná-lo limpo e testável. Você pode substituir um relógio falso pelo invólucro e definir o horário para o que quiser. Problema resolvido.


2

Como Doval disse, este exemplo provavelmente está apenas tentando apresentá-lo às funções. Eu sou geral, porém, é útil. Especialmente, especificando alguns argumentos, mas nem todos, e passando os outros, você pode criar uma função mais específica de caso a partir de uma função mais geral. Como um exemplo um tanto trivial, considere uma função de classificação que leva uma matriz para classificar e uma função comparadora para classificar. Ao especificar uma função comparadora que se compara ao valor numérico, posso criar uma função sortByNumericalValue, e as chamadas para essa função são muito mais claras e concisas.


2

O pensamento por trás de sua pergunta parece ser: "Por que não escrever alert("Hello");diretamente? É bem simples".

A resposta é, em parte, porque você realmente não quer ligar alert("Hello")- você só quer dizer olá.

Ou: Por que armazenar contatos no telefone, quando você pode apenas discar números de telefone? Porque você não quer se lembrar de todos esses números; porque discar números é tedioso e propenso a erros; porque o número pode mudar, mas ainda é a mesma pessoa do outro lado. Porque você deseja ligar para pessoas , não para números.

É o que entendemos por termos como abstração, indireção, "ocultando detalhes de implementação" e até "código expressivo".

O mesmo raciocínio se aplica ao uso de constantes em vez de escrever valores brutos em qualquer lugar. Você pode escrever 3.141592...sempre que precisar π, mas felizmente existe Math.PI.

Também podemos nos olhar alert(). Quem se importa como ele constrói e exibe esse diálogo de alerta? Você só quer alertar o usuário. Entre você escrevendo alert("Hello")e os pixels na tela mudando, há uma pilha profunda e profunda de código e hardware, com cada camada informando a próxima o que ela deseja e a próxima camada cuidando dos detalhes até que a camada mais profunda possível inverta alguns bits no memória de vídeo.

Você realmente não quer fazer tudo isso sozinho para dizer olá.

Programar - bem, qualquer tarefa, na verdade - tem tudo a ver com dividir problemas complexos em partes gerenciáveis. A resolução de cada parte fornece um bloco de construção e, com blocos de construção simples o suficiente, você pode construir coisas grandes.

É álgebra.


-1

É uma boa ideia manter o cálculo dos valores (expressões) separado da execução das ações (instruções). Queremos um controle preciso sobre onde e quando as ações serão executadas (como exibir mensagens), mas ao calcular valores, preferimos trabalhar em um nível mais abstrato e não ter que nos preocupar com o cálculo desses valores.

Uma função que calcula apenas um valor de retorno, usando apenas os argumentos fornecidos, é chamada pura .

Uma "função" que executa uma ação é na verdade um procedimento que tem efeito .

Quaisquer efeitos causados ​​durante o cálculo de um valor são chamados efeitos colaterais , e é melhor evitá-los sempre que possível ("Eu só precisava dessa sequência, não sabia que isso iria prejudicar o banco de dados!").

Para minimizar a chance de efeitos colaterais, devemos evitar enviar muitos dados para nossos procedimentos ou colocar qualquer cálculo neles; se algum cálculo precisar ser realizado antecipadamente, geralmente é melhor fazê-lo separadamente em uma função pura e passar apenas o resultado necessário ao procedimento. Isso mantém claro o objetivo do procedimento e reduz a chance de ser reutilizado posteriormente como parte de um cálculo (a função pura pode ser reutilizada).

Pelo mesmo motivo, devemos evitar o processamento de resultados dentro de um procedimento. É melhor retornar o resultado (se houver) nossa ação e executar qualquer processamento subsequente com funções puras.

Se seguirmos essas regras, poderemos terminar com um procedimento como o sayHelloqual não precisa de dados e não tem resultado. Portanto, a melhor interface para isso é não ter argumentos e não retornar um valor. É preferível, por exemplo, chamar "console.log" no meio de algum cálculo.

Para reduzir a necessidade de efeitos durante o cálculo, podemos fazer cálculos que retornam procedimentos ; por exemplo. se precisarmos decidir sobre uma ação a ser tomada, podemos ter uma função pura, escolher um procedimento e devolvê-lo, em vez de executá-lo diretamente.

Da mesma forma, para reduzir a necessidade de cálculo durante os procedimentos, podemos fazer com que os procedimentos tomem outros procedimentos como parâmetros (possivelmente o resultado de uma função); por exemplo. tomando uma variedade de procedimentos e executando um após o outro.


Você parece estar defendendo a OOP aqui, onde o princípio fundamental é "diga, não pergunte". Seguir o conselho aqui é o que normalmente leva ao código repleto de chamadas inúteis "getFoo", "setFoo" e "execute". OOP significa combinar dados e comportamento, não separá-los.
Aaronaught

@Aaronaught É verdade que sou contra a OOP, mas certamente não recomendaria setFooligações, pois isso implica em dados mutáveis. Alterar o conteúdo de variáveis ​​/ propriedades é um efeito, que faz com que todos os cálculos que usam esses dados se tornem impuros. Eu não recomendaria executechamadas por si só, mas eu iria recomendar runFooprocedimentos para a execução de ações com base em seus argumentos.
Warbo

-2

Eu diria que é uma combinação das respostas dadas por @MichaelT e @Sleiman Jneidi.

Talvez haja vários lugares no código em que você deseja cumprimentar o usuário com uma mensagem Olá, para que você agrupe essa ideia em um método. No método, você pode traduzi-lo ou expandir um simples 'Olá' exibindo um texto mais longo. Além disso, convém usar uma boa caixa de diálogo JQuery no lugar de um alerta.

O ponto é que a implementação está em um só lugar. Você só precisa alterar o código em um só lugar. (SayHello () é talvez um exemplo muito simples)


@ downvoter (s) - compartilhe seu conhecimento com o resto de nós. Minha postagem está simplesmente errada, mal escrita ou o quê?
paul
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.