Qual è il vantaggio di una funzione senza parametri che chiama solo un'altra funzione


21

Un tutorial (per Javascript) che sto facendo suggerisce di scrivere una funzione come questa:

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

Oltre all'offuscamento ci sono vantaggi nello scrivere qualcosa di simile nella vita reale? In tal caso, quali sono i vantaggi?


14
Probabilmente sta solo cercando di mostrarti come definire le tue funzioni.
Doval,

4
Non scriverei mai il codice in modo tale da offuscarlo - il codice è per il programmatore che legge facilmente, non viceversa. Usa un offuscatore per offuscare.
Rhughes

32
Il vantaggio è che avvolge una funzione con parametri. In questo modo, se in seguito desideri rendere la tua app spagnola, devi solo cambiare "Ciao" in "Ola" in un unico posto.
Pieter B,

9
@PieterB in realtà, "Hola"
rev

29
@PieterB - E se scopri di aver sbagliato a scrivere "Hola" devi solo sistemarlo in un posto.
Daniel R Hicks,

Risposte:


60

Per favore, scusami se ho questo errore ... Javascript non è la mia lingua preferita di implementazione.

Ci sono diversi motivi per cui si vorrebbe avere una funzione no arg per avvolgere un'altra chiamata di funzione. Mentre la semplice chiamata a window.alert("Hello");è qualcosa che potresti immaginare, invece di chiamare direttamente invece di sayHello().

E se ci fosse di più? Hai una dozzina di posti in cui vuoi chiamare sayHello()e window.alert("Hello");invece hai scritto . Ora vuoi che faccia un window.alert("Hello, it is now " + new Date()). Se hai terminato tutte quelle chiamate mentre le sayHello()cambi in un posto. Altrimenti, lo cambi in una dozzina di posti. Questo tocca Non ripetere te stesso . Lo fai perché non vuoi farlo una dozzina di volte in futuro.

In passato ho lavorato con una libreria i18n / l10n che utilizzava funzioni per localizzare il testo sul lato client. Considera la sayHello()funzione. Potresti farlo stampare holaquando l'utente è localizzato in una lingua spagnola. Questo potrebbe assomigliare a:

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

Tuttavia, non è così che funzionava la biblioteca. Invece, aveva una serie di file che assomigliavano a:

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

Quindi la libreria rileverà l'impostazione della lingua del browser e quindi creerebbe funzioni dinamiche con la localizzazione appropriata come valore di ritorno per una chiamata di funzione senza argomenti in base al file di localizzazione appropriato.

Non sono abbastanza programmatore Javascript per dire se è buono o cattivo ... solo che era e può essere visto come un possibile approccio.

Il punto è che avvolgere la chiamata a un'altra funzione in una sua funzione è spesso abbastanza utile e aiuta con la modularizzazione dell'applicazione e può anche risultare più facile da leggere il codice.

A parte questo, stai lavorando da un tutorial. È necessario introdurre le cose il più semplicemente possibile all'inizio. L'introduzione di chiamate di funzione in stile vararg sin dall'inizio può comportare un codice molto confuso per una persona che non ha familiarità con la codifica in generale. È molto più facile passare da nessun argomento, a argomenti, allo stile di varargs - con ogni costruzione sugli esempi e sulla comprensione precedenti.


3
window.alertviene spesso utilizzato come segnaposto durante lo sviluppo fino a quando non è possibile progettare / implementare un bel modale / popup, quindi, come per il problema della lingua, può essere commutato molto più facilmente. Oppure, se ne hai già uno, un aggiornamento del design di qualche anno lungo potrebbe richiedere modifiche simili.
Izkata,

34

Penso che a volte sia utile per nascondere l'implementazione.

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

E questo ti dà la flessibilità di cambiarlo in seguito

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

19

Oltre all'offuscamento ci sono vantaggi nello scrivere qualcosa di simile nella vita reale? In tal caso, quali sono i vantaggi?

  • centralizzazione: sebbene l'implementazione sia lunga una sola riga, se è una linea che cambia spesso, probabilmente preferisci cambiarla in un unico posto, piuttosto che ovunque si chiami Hello.

  • minimizzare / nascondere le dipendenze: il codice client non deve più sapere che esiste un oggetto window e puoi persino modificare l'intera implementazione senza influire affatto sul codice client

  • adempimento del contratto: il codice client potrebbe prevedere un modulo con una funzione sayHello. In questo caso, anche se banale, la funzione deve essere lì.

  • coerenza dei livelli di astrazione: se il codice client utilizza operazioni di alto livello, è nel tuo interesse scrivere il codice client in termini di 'sayHello, sayBye' e alcune altre funzioni 'sayXXX', anziché oggetti della finestra. Infatti, nel codice client potresti non voler nemmeno sapere che esiste un oggetto "finestra".


Mi piace questa risposta. Spesso la progettazione è la ragione principale di queste funzioni, in particolare la progettazione OO in cui si desidera che gli oggetti responsabili del comportamento eseguano funzioni di altri oggetti con un comportamento specifico. Immagina la tua auto, in particolare i cambi di marcia. Sposta la marcia in alto "dicendo" al tuo bastone di cambiare marcia. A sua volta inoltra questa chiamata al tuo cambio. Inoltre, controlla prima che tu possa spostarti dalla tua posizione attuale.
Eric,

3
Le altre ragioni sono buone ma +1 per menzionare i livelli di astrazione. L'uso di astrazioni chiare e nomi di buone funzioni può semplificare molto la lettura e la manutenzione del vecchio codice. Nel punto in cui viene chiamato sayHello (), non sei necessariamente preoccupato di come è implementato, vuoi solo capire qual è l'intenzione di questa linea di codice. E un nome di funzione come sayHello () lo rende ovvio.
kkrambo,

Anche +1 per menzionare i livelli di astrazione: può accadere che l'implementazione di una funzione a un livello di astrazione consista in una chiamata a un'altra funzione di un livello di astrazione inferiore. E allora?
Giorgio,

7

Incredibile che nessuna altra persona abbia menzionato i test.

La particolare linea "incartata" che hai scelto window.alert('hello'), è in realtà un perfetto esempio di questo. Tutto ciò che coinvolge l' windowoggetto è davvero, davvero doloroso da testare. Moltiplicalo per 1000 volte nella tua applicazione e garantisco che gli sviluppatori alla fine rinunceranno ai test. D'altra parte, è abbastanza facile ostruire la sayHellofunzione con una spia e testare che è stata chiamata.

Un esempio più pratico - perché davvero, chi utilizza effettivamente window.alert(...)nel codice di produzione? - sta controllando l'orologio di sistema. Quindi, per esempio, questo dovrebbe essere incluso DateTime.Nowin .NET, time(...)in C / C ++ o System.currentTimeMillis()in Java. Vuoi davvero avvolgere quelli in una dipendenza che puoi iniettare, perché non sono solo (quasi) impossibili da deridere / falsificare, sono di sola lettura e non deterministici . Qualsiasi test che copra una funzione o un metodo che fa uso diretto della funzione di orologio di sistema è estremamente soggetto a guasti intermittenti e / o casuali.

Il wrapper effettivo è una funzione a 1 riga - return DateTime.Now- ma è tutto ciò che serve per prendere un oggetto mal progettato, non testabile e renderlo pulito e verificabile. Puoi sostituire un orologio falso per il wrapper e impostare l'ora su ciò che desideri. Problema risolto.


2

Come ha detto Doval, questo esempio probabilmente sta solo cercando di farti conoscere le funzioni. Sono generale, tuttavia, è utile. Soprattutto specificando alcuni ma non tutti gli argomenti, e passando gli altri, è possibile effettuare una funzione più specifica del caso da una funzione più generale. Come esempio un po 'banale, considera una funzione di ordinamento che richiede un array per ordinare e una funzione di confronto con cui ordinare. Specificando una funzione di confronto che confronta per valore numerico, posso creare una funzione sortByNumericalValue e le chiamate a tale funzione sono molto più chiare e concise.


2

Il pensiero dietro la tua domanda sembra essere: "Perché non scrivere alert("Hello");direttamente? È abbastanza semplice".

La risposta è, in parte, perché non vuoi davvero chiamare alert("Hello"), vuoi solo salutare.

Oppure: perché archiviare i contatti sul telefono, quando è possibile comporre i numeri di telefono? Perché non vuoi ricordare tutti quei numeri; perché la composizione dei numeri è noiosa e soggetta a errori; perché il numero potrebbe cambiare, ma è sempre la stessa persona all'altro capo. Perché vuoi chiamare le persone , non i numeri.

Questo è ciò che si intende con termini come astrazione, riferimento indiretto, "nascondere i dettagli dell'implementazione" e persino "codice espressivo".

Lo stesso ragionamento si applica all'utilizzo delle costanti anziché alla scrittura di valori grezzi ovunque. Si può solo scrivere 3.141592...ogni volta che avete bisogno π, ma per fortuna non c'è Math.PI.

Potremmo anche guardare alert()se stesso. A chi importa come costruisce e visualizza quella finestra di avviso? Vuoi solo avvisare l'utente. Tra la scrittura alert("Hello")e i pixel sullo schermo che cambiano, c'è una pila profonda e profonda di codice e hardware, con ogni livello che dice al prossimo ciò che vuole, e quel livello successivo si prende cura dei dettagli fino a quando il livello più profondo possibile lancia alcuni bit nel memoria video.

Davvero non devi fare tutto da solo per salutare.

La programmazione - beh, qualsiasi compito, davvero - si basa sulla suddivisione di problemi complessi in blocchi gestibili. Risolvere ogni blocco ti dà un blocco e con abbastanza semplici blocchi puoi costruire grandi cose.

È algebra.


-1

È una buona idea mantenere il calcolo dei valori (espressioni) separato dall'esecuzione delle azioni (istruzioni). Vogliamo un controllo preciso su dove e quando verranno intraprese le azioni (come la visualizzazione di messaggi), ma quando calcoliamo i valori preferiremmo lavorare a un livello più astratto e non dovremmo preoccuparci di come vengono calcolati quei valori.

Una funzione che calcola solo un valore di ritorno, usando solo gli argomenti forniti, è chiamata pura .

Una "funzione" che esegue un'azione è in realtà una procedura , che ha un effetto .

Tutti gli effetti causati durante il calcolo di un valore sono chiamati effetti collaterali , ed è meglio evitarli ove possibile ("Avevo solo bisogno di quella stringa, non sapevo che avrebbe martellato il database!").

Per ridurre al minimo la possibilità di effetti collaterali, dovremmo evitare di inviare troppi dati alle nostre procedure o di inserire alcun calcolo in esse; se è necessario eseguire preventivamente alcuni calcoli, di solito è meglio farlo separatamente in una funzione pura, quindi passare solo il risultato richiesto alla procedura. Ciò mantiene chiaro lo scopo della procedura e riduce la possibilità che venga riutilizzato in seguito come parte di un calcolo (la funzione pura può invece essere riutilizzata).

Per lo stesso motivo, dovremmo evitare di elaborare i risultati all'interno di una procedura. È meglio restituire il risultato (se presente) alla nostra azione ed eseguire qualsiasi elaborazione successiva con funzioni pure.

Se seguiamo queste regole, potremmo finire con una procedura simile sayHello, che non ha bisogno di dati e non ha risultati. Quindi la migliore interfaccia per esso è di non avere argomenti e non restituire un valore. È preferibile, ad esempio, chiamare "console.log" nel mezzo di alcuni calcoli.

Per ridurre la necessità di effetti durante il calcolo, possiamo avere calcoli che restituiscono procedure ; per esempio. se dobbiamo decidere un'azione da intraprendere, possiamo avere una pura funzione scegliere una procedura e restituirla, anziché eseguirla direttamente.

Allo stesso modo, per ridurre la necessità di calcoli durante le procedure, possiamo fare in modo che le procedure prendano altre procedure come parametri (possibilmente il risultato di una funzione); per esempio. prendendo una serie di procedure ed eseguendo una dopo l'altra.


Sembra che tu stia sostenendo contro OOP qui, dove il principio fondamentale è "dire, non chiedere". Seguire i consigli qui è ciò che normalmente porta al codice pieno di inutili chiamate "getFoo", "setFoo" ed "esegui". OOP consiste nel combinare dati e comportamento, non nel separarli.
Aaronaught il

@Aaronaught È vero che sono contrario a OOP, ma certamente non consiglierei le setFoochiamate, poiché ciò implica dati mutabili. La modifica del contenuto di variabili / proprietà è un effetto che rende impuri tutti i calcoli che utilizzano tali dati. Non vorrei raccomandare executele chiamate di per sé, ma io vorrei raccomandare runFoole procedure per l'esecuzione di azioni in base alle loro argomentazioni.
Warbo,

-2

Direi che è una combinazione delle risposte fornite da @MichaelT e @Sleiman Jneidi.

Forse ci sono un certo numero di posizioni nel codice in cui si desidera salutare l'utente con un messaggio Hello in modo da impacchettare quell'idea in un metodo. Nel metodo puoi tradurlo o espanderlo in un semplice "Ciao" visualizzando un testo più lungo. Inoltre potresti voler usare una bella finestra di dialogo JQuery al posto di un avviso.

Il punto è che l'implementazione è in un posto. Hai solo bisogno di cambiare il codice in un unico posto. (SayHello () è forse un esempio troppo semplice)


@downvoter (s) - cura di condividere le tue conoscenze con il resto di noi. Il mio post è semplicemente sbagliato, scritto male o cosa?
paul
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.