¿Cuál es el beneficio de una función sin parámetros que solo llama a otra función?

21

Un tutorial (para Javascript) que estoy haciendo sugiere que escribamos una función como esta:

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

Aparte de la ofuscación, ¿hay beneficios al escribir algo como esto en la vida real? Si es así, ¿cuáles son los beneficios?

Daniel
fuente
14
Probablemente solo esté tratando de mostrarle cómo definir sus propias funciones.
Doval
44
Nunca escribiría código de tal manera que lo ofuscara: el código es para que el programador lo lea fácilmente, no al revés. Use un ofuscador para ofuscar.
Rhughes
32
El beneficio es que envuelve una función con parámetros. De esta manera, si luego desea que su aplicación sea española, solo tiene que cambiar "Hola" a "Ola" en un solo lugar.
Pieter B
99
@PieterB en realidad, "Hola"
rev
29
@PieterB - Y si descubres que escribiste mal "Hola", solo tienes que arreglarlo en un solo lugar.
Daniel R Hicks

Respuestas:

60

Perdone mi memoria si tengo esto incorrecto ... Javascript no es mi lenguaje de implementación preferido.

Hay varias razones por las cuales uno desearía que una función sin arg envuelva otra llamada de función. Si bien la simple llamada a window.alert("Hello");es algo que podrías imaginar simplemente llama directamente en lugar de sayHello().

Pero, ¿y si hay más? Tienes una docena de lugares donde quieres llamar sayHello()y has escrito en su window.alert("Hello");lugar. Ahora quieres que haga un window.alert("Hello, it is now " + new Date()). Si envuelve todas esas llamadas mientras sayHello()lo cambia en un solo lugar. Si no lo hiciste, lo cambias en una docena de lugares. Esto toca No te repitas . Lo haces porque no quieres tener que hacerlo una docena de veces en el futuro.

Trabajé con una biblioteca i18n / l10n en el pasado que usaba funciones para hacer la localización del texto del lado del cliente. Considera la sayHello()función. Podría imprimirlo holacuando el usuario esté localizado en un idioma español. Esto podría verse algo así como:

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

Sin embargo, no es así como funcionaba la biblioteca. En cambio, tenía un conjunto de archivos que se veían así:

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

Y luego la biblioteca detectaría la configuración del idioma del navegador y luego crearía funciones dinámicas con la localización apropiada como el valor de retorno para una llamada de función sin argumentos basada en el archivo de localización apropiado.

No soy lo suficientemente codificador de Javascript para decir si eso es bueno o malo ... solo que fue y puede ser visto como un posible enfoque.

El punto es que envolver la llamada a otra función en una función propia a menudo es bastante útil y ayuda con la modularización de la aplicación y también puede resultar en un código más fácil de leer.

Aparte de eso, estás trabajando en un tutorial. Es necesario introducir las cosas lo más simple posible al principio. La introducción de llamadas de función de estilo varargs desde el principio puede resultar en un código muy confuso para una persona que no está familiarizada con la codificación en general. Es mucho más fácil pasar de ningún argumento, a argumentos, al estilo varargs, cada uno de los cuales se basa en los ejemplos y la comprensión anteriores.

zzzzBov
fuente
3
window.alertTambién se usa a menudo como marcador de posición durante el desarrollo hasta que se pueda diseñar / implementar un buen modo / ventana emergente, por lo que, al igual que el problema del idioma, se puede cambiar mucho más fácilmente. O si ya tiene uno, una actualización de diseño en unos años podría requerir cambios similares.
Izkata
34

Creo que a veces es útil para ocultar la implementación.

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

Y esto te da la flexibilidad para cambiarlo más tarde

 function sayHello() {
  console.log("Hello");
 }
Sleiman Jneidi
fuente
19

Aparte de la ofuscación, ¿hay beneficios al escribir algo como esto en la vida real? Si es así, ¿cuáles son los beneficios?

  • centralización: aunque la implementación es una sola línea larga, si es una línea que cambia con frecuencia, probablemente prefiera cambiarla en un solo lugar, que donde sea que se llame sayHello.

  • minimizar / ocultar dependencias: su código de cliente ya no necesita saber que hay un objeto de ventana, e incluso puede cambiar toda la implementación sin afectar el código del cliente

  • cumplimiento del contrato: el código del cliente puede esperar un módulo que tenga una función sayHello. En este caso, incluso si es trivial, entonces la función debe estar allí.

  • consistencia de los niveles de abstracción: si el código del cliente utiliza operaciones de alto nivel, le interesa escribir el código del cliente en términos de 'sayHello, sayBye' y algunas otras funciones 'sayXXX', en lugar de objetos de ventana. De hecho, en el código del cliente, es posible que ni siquiera quiera saber que existe un objeto 'ventana'.

utnapistim
fuente
Me gusta esta respuesta A menudo, el diseño es la razón principal de estas funciones, especialmente el diseño OO donde desea que los objetos responsables del comportamiento ejecuten funciones de otros objetos con un comportamiento específico. Imágenes de su automóvil, más particularmente cambios de marchas. Cambias tu marcha "diciéndole" a tu palanca que suba. A su vez, reenvía esta llamada a su caja de cambios. Pero además, primero verifica que puede cambiar desde su posición actual.
Eric
3
Las otras razones son buenas pero +1 para mencionar los niveles de abstracción. El uso de abstracciones claras y buenos nombres de funciones puede hacer que sea mucho más fácil leer y mantener el código antiguo. En el punto donde se llama a sayHello (), no está necesariamente preocupado por cómo se implementa, solo desea comprender cuál es la intención de esta línea de código. Y un nombre de función como sayHello () lo hace obvio.
kkrambo
También +1 por mencionar niveles de abstracción: puede suceder que la implementación de una función en un nivel de abstracción consista en una llamada a otra función de un nivel de abstracción más bajo. ¿Y qué?
Giorgio
7

Es sorprendente que ninguna otra persona haya mencionado las pruebas.

La línea particular "envuelta" que elegiste window.alert('hello'), es en realidad un ejemplo perfecto de esto. Cualquier cosa que involucre al windowobjeto es muy, muy doloroso de probar. Multiplique eso por 1000 veces en su aplicación y le garantizo que los desarrolladores finalmente abandonarán las pruebas. Por otro lado, es bastante fácil simplemente golpear la sayHellofunción con un espía y probar que se llamó.

Un ejemplo más práctico: porque realmente, ¿quién lo usa realmente window.alert(...)en el código de producción? - está comprobando el reloj del sistema. Entonces, por ejemplo, esto estaría envolviendo DateTime.Nowen .NET, time(...)en C / C ++ o System.currentTimeMillis()en Java. Usted realmente desea para envolver los de una dependencia que se puede inyectar, porque no sólo son (casi) imposible burlarse / falso, de sólo lectura que son y no determinista . Cualquier prueba que cubra una función o método que haga uso directo de la función del reloj del sistema es extremadamente probable que sufra fallas intermitentes y / o aleatorias.

El envoltorio real es una función de 1 línea return DateTime.Now, pero eso es todo lo que necesita para tomar un objeto no comprobable y mal diseñado y convertirlo en uno limpio y comprobable. Puede sustituir un reloj falso por el envoltorio y configurar su hora a lo que desee. Problema resuelto.

Aaronaught
fuente
2

Como dijo Doval, este ejemplo probablemente solo trate de presentarle las funciones. Soy general, sin embargo, es útil. Especialmente especificando algunos, pero no todos, los argumentos, y pasando los otros, puede hacer una función más específica de caso a partir de una función más general. Como un ejemplo algo trivial, considere una función de clasificación que requiere una matriz para ordenar y una función de comparación para ordenar. Al especificar una función de comparación que se compara por valor numérico, puedo hacer una función sortByNumericalValue, y las llamadas a esa función son mucho más claras y concisas.

raptortech97
fuente
2

El pensamiento detrás de su pregunta parece ser: "¿Por qué no simplemente escribir alert("Hello");directamente? Es bastante simple".

La respuesta es, en parte, porque realmente no quieres llamar alert("Hello"), solo quieres saludar.

O: ¿Por qué almacenar contactos en su teléfono, cuando solo puede marcar números de teléfono? Porque no quieres recordar todos esos números; porque marcar números es tedioso y propenso a errores; porque el número puede cambiar, pero sigue siendo la misma persona en el otro extremo. Porque quieres llamar a personas , no a números.

Esto es lo que se entiende por términos como abstracción, indirección, "ocultar detalles de implementación" e incluso "código expresivo".

El mismo razonamiento se aplica al uso de constantes en lugar de escribir valores brutos en todas partes. Usted podría simplemente escribir 3.141592...cada vez que necesita π, pero por suerte hay Math.PI.

También podríamos mirarnos a nosotros alert()mismos. ¿A quién le importa cómo construye y muestra ese diálogo de alerta? Solo desea alertar al usuario. Entre su escritura alert("Hello")y el cambio de píxeles en su pantalla, hay una pila profunda y profunda de código y hardware, cada capa le dice a la siguiente lo que quiere, y esa siguiente capa se ocupa de los detalles hasta que la capa más profunda posible voltea algunos bits en el memoria de video.

Realmente no quieres tener que hacer todo eso solo para saludar.

La programación, bueno, cualquier tarea, realmente se trata de dividir problemas complejos en partes manejables. Resolver cada fragmento te da un bloque de construcción, y con suficientes bloques de construcción simples, puedes construir cosas grandes.

Es álgebra.

Flambino
fuente
-1

Es una buena idea mantener el cálculo de valores (expresiones) separado de la ejecución de acciones (declaraciones). Queremos un control preciso sobre dónde y cuándo se tomarán acciones (como mostrar mensajes), pero al calcular los valores, preferimos trabajar a un nivel más abstracto y no tener que preocuparnos por cómo se calculan esos valores.

Una función que solo calcula un valor de retorno, usando solo los argumentos que se dan, se llama pura .

Una "función" que realiza una acción es en realidad un procedimiento , que tiene un efecto .

Todos los efectos causados ​​durante el cálculo de un valor se denominan efectos secundarios , y es mejor evitarlos cuando sea posible ("¡Solo necesitaba esa cadena, no sabía que dañaría la base de datos!").

Para minimizar la posibilidad de efectos secundarios, debemos evitar enviar demasiados datos a nuestros procedimientos, o poner algún cálculo en ellos; Si es necesario realizar algún cálculo de antemano, generalmente es mejor hacerlo por separado en una función pura, luego pasar solo el resultado requerido al procedimiento. Esto mantiene el propósito del procedimiento claro y reduce la posibilidad de que sea reutilizado más tarde como parte de un cálculo (la función pura se puede reutilizar en su lugar).

Por la misma razón, debemos evitar procesar los resultados dentro de un procedimiento. Es mejor devolver el resultado (si lo hay) a nuestra acción y realizar cualquier procesamiento posterior con funciones puras.

Si seguimos estas reglas, podríamos terminar con un procedimiento como sayHello, que no necesita ningún dato y no tiene un resultado. Por lo tanto, la mejor interfaz es no tener argumentos y no devolver un valor. Esto es preferible a, por ejemplo, llamar a "console.log" en medio de algún cálculo.

Para reducir la necesidad de efectos durante el cálculo, podemos tener cálculos que devuelvan procedimientos ; p.ej. Si necesitamos decidir qué acción tomar, podemos tener una función pura, elegir un procedimiento y devolverlo, en lugar de ejecutarlo directamente.

Del mismo modo, para reducir la necesidad de cálculo durante los procedimientos, podemos hacer que los procedimientos tomen otros procedimientos como parámetros (posiblemente el resultado de una función); p.ej. tomando una serie de procedimientos y ejecutándose uno tras otro.

Warbo
fuente
Parece que está abogando contra la POO aquí, donde el principio fundamental es "decir, no preguntar". Seguir el consejo aquí es lo que normalmente conduce a un código plagado de inútiles llamadas "getFoo", "setFoo" y "execute". OOP se trata de combinar datos y comportamiento, no separarlos.
Aaronaught
@Aaronaught Es cierto que estoy en contra de la POO, pero ciertamente no recomendaría setFoollamadas, ya que eso implica datos mutables. La alteración del contenido de las variables / propiedades es un efecto que hace que todos los cálculos que utilizan esos datos se vuelvan impuros. No recomendaría executellamadas per se, pero me gustaría recomendar runFooprocedimientos para la ejecución de acciones sobre la base de sus argumentos.
Warbo
-2

Diría que es una combinación de las respuestas dadas por @MichaelT y @Sleiman Jneidi.

Tal vez hay varios lugares en el código donde desea saludar al usuario con un mensaje de saludo para que pueda empaquetar esa idea en un método. En el método, puede traducirlo o expandirlo en un simple 'Hola' mostrando un texto más largo. También es posible que desee utilizar un buen cuadro de diálogo JQuery en lugar de una alerta.

El punto es que la implementación está en un solo lugar. Solo necesita cambiar el código en un solo lugar. (SayHello () es quizás un ejemplo demasiado simple)

Paul
fuente
@downvoter (s): cuídate de compartir tus conocimientos con el resto de nosotros. ¿Mi publicación es simplemente incorrecta, está mal escrita o qué?
Paul