Jaka jest korzyść z funkcji bez parametrów, która wywołuje tylko inną funkcję

21

Samouczek (dla Javascript), który wykonuję, sugeruje napisanie takiej funkcji:

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

Czy oprócz zaciemnienia istnieją korzyści z pisania czegoś takiego w prawdziwym życiu? Jeśli tak, jakie są korzyści?

Daniel
źródło
14
Prawdopodobnie próbuje tylko pokazać, jak zdefiniować własne funkcje.
Doval
4
Nigdy nie napisałbym kodu w taki sposób, aby go zaciemnić - kod jest dla programisty łatwy do odczytania, a nie na odwrót. Do zaciemnienia użyj obfuscater.
ruguje
32
Zaletą jest to, że owija funkcję z parametrami. W ten sposób, jeśli chcesz później uczynić swoją aplikację hiszpańską, wystarczy zmienić „Witaj” na „Ola” w jednym miejscu.
Pieter B
9
@ PieterB faktycznie, „Hola”
rev
29
@PieterB - A jeśli odkryjesz, że źle napisałeś „Hola”, musisz to naprawić tylko w jednym miejscu.
Daniel R Hicks

Odpowiedzi:

60

Proszę wybacz mi pamięć, jeśli mam ten niepoprawny ... JavaScript nie jest moim preferowanym językiem implementacji.

Istnieje kilka powodów, dla których ktoś chciałby, aby funkcja no arg zawijała inne wywołanie funkcji. Chociaż proste wezwanie do window.alert("Hello");jest czymś, co można sobie wyobrazić, zamiast tego dzwoniąc bezpośrednio zamiast sayHello().

Ale co, jeśli jest coś więcej? Masz tuzin miejsc, do których chcesz zadzwonić sayHello()i window.alert("Hello");zamiast tego napisałeś . Teraz chcesz to zrobić window.alert("Hello, it is now " + new Date()). Jeśli wszystkie te połączenia zostały opakowane, gdy sayHello()zmienisz je w jednym miejscu. Jeśli tego nie zrobiłeś, zmieniasz go w kilkunastu miejscach. To dotyczy „ Nie powtarzaj się” . Robisz to, ponieważ nie chcesz tego robić kilkanaście razy w przyszłości.

W przeszłości pracowałem z biblioteką i18n / l10n, która używała funkcji do lokalizacji tekstu po stronie klienta. Rozważ sayHello()funkcję. Możesz go wydrukować, holagdy użytkownik jest zlokalizowany na język hiszpański. Może to wyglądać mniej więcej tak:

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

Chociaż nie tak działała biblioteka. Zamiast tego miał zestaw plików, które wyglądały następująco:

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

Następnie biblioteka wykryje ustawienia języka przeglądarki, a następnie utworzy funkcje dynamiczne z odpowiednią lokalizacją jako wartość zwracaną dla wywołania funkcji bez kłótni na podstawie odpowiedniego pliku lokalizacji.

Nie jestem wystarczającym koderem Javascript, aby powiedzieć, czy to dobrze, czy źle ... po prostu to było i może być postrzegane jako możliwe podejście.

Chodzi o to, że zawinięcie wywołania innej funkcji w jej własną funkcję jest często bardzo przydatne i pomaga w modularyzacji aplikacji, a także może skutkować łatwiejszym odczytem kodu.

Poza tym pracujesz z samouczka. Na początku należy wprowadzić rzeczy tak prosto, jak to możliwe. Wprowadzenie wywołań funkcji stylu varargs od samego początku może spowodować bardzo mylący kod dla osoby, która nie jest zaznajomiona z kodowaniem w ogóle. O wiele łatwiej jest przejść od braku argumentu, do argumentów, do stylu varargs - z każdym budowaniem na poprzednich przykładach i zrozumieniu.

zzzzBov
źródło
3
window.alertjest również często używany jako symbol zastępczy podczas programowania, dopóki nie można zaprojektować / wdrożyć ładnego modalu / wyskakującego okienka, więc podobnie jak w przypadku języka, można go znacznie łatwiej wyłączyć. Lub jeśli już go masz, aktualizacja projektu kilka lat później może wymagać podobnych zmian.
Izkata
34

Myślę, że czasami jest to przydatne do ukrywania implementacji.

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

A to daje elastyczność, aby zmienić to później

 function sayHello() {
  console.log("Hello");
 }
Sleiman Jneidi
źródło
19

Czy oprócz zaciemnienia istnieją korzyści z pisania czegoś takiego w prawdziwym życiu? Jeśli tak, jakie są korzyści?

  • centralizacja: chociaż implementacja jest długa na jedną linię, jeśli jest to linia, która często się zmienia, prawdopodobnie wolisz ją zmienić w jednym miejscu, niż gdziekolwiek nazywa się Hello.

  • minimalizowanie / ukrywanie zależności: kod klienta nie musi już wiedzieć, że istnieje obiekt okna, a nawet możesz zmienić całą implementację bez wpływu na kod klienta

  • realizacja umowy: kod klienta może oczekiwać modułu z funkcją sayHello. W tym przypadku, nawet jeśli banalna, funkcja musi tam być.

  • spójność poziomów abstrakcji: jeśli kod klienta korzysta z operacji wysokiego poziomu, w twoim interesie jest napisanie kodu klienta w kategoriach „sayHello, sayBye” i niektórych innych funkcji (sayXXX) zamiast obiektów okna. Rzeczywiście, w kodzie klienta możesz nawet nie chcieć wiedzieć, że istnieje coś takiego jak obiekt „okna”.

utnapistim
źródło
Podoba mi się ta odpowiedź. Często projektowanie jest głównym powodem tych funkcji, szczególnie projektowanie OO, w którym obiekty odpowiedzialne za zachowanie wykonują funkcje innych obiektów o określonym zachowaniu. Obrazowanie samochodu, a zwłaszcza zmiany biegów. Zmieniasz bieg, „każąc” kijowi, aby się podniósł. To z kolei przekazuje to połączenie do twojej skrzyni biegów. Ale dodatkowo sprawdza najpierw, czy możesz zmienić pozycję z aktualnej pozycji.
Eric
3
Inne powody są dobre, ale +1 za wzmiankę o poziomach abstrakcji. Używanie wyraźnych abstrakcji i dobrych nazw funkcji może znacznie ułatwić odczyt i obsługę starego kodu. W miejscu, w którym wywoływana jest funkcja sayHello (), niekoniecznie musisz się martwić, w jaki sposób jest ona implementowana, po prostu chcesz zrozumieć, jaki jest zamiar tego wiersza kodu. A nazwa funkcji, taka jak sayHello (), czyni to oczywistym.
kkrambo,
Również +1 za wzmiankę o poziomach abstrakcji: może się zdarzyć, że implementacja funkcji na jednym poziomie abstrakcji składa się z jednego wywołania innej funkcji niższego poziomu abstrakcji. Więc co?
Giorgio
7

Zadziwiające, że żadna inna osoba nie wspomniała o testowaniu.

Ta konkretna „owinięta” linia, którą wybrałeś window.alert('hello'), jest w rzeczywistości doskonałym tego przykładem. Testowanie wszystkiego, co dotyczy tego windowobiektu, jest naprawdę bardzo bolesne. Pomnóż to przez 1000 razy w swojej aplikacji i gwarantuję, że programiści ostatecznie zrezygnują z testowania. Z drugiej strony dość łatwo jest po prostu zablokować sayHellofunkcję szpiegiem i przetestować, że została wywołana.

Bardziej praktyczny przykład - bo tak naprawdę, kto tak naprawdę używa window.alert(...)w kodzie produkcyjnym? - sprawdza zegar systemowy. Na przykład byłoby to zawijanie DateTime.Noww .NET, time(...)w C / C ++ lub System.currentTimeMillis()w Javie. Ty naprawdę chcesz zawinąć te w zależności, które można wstrzykiwać, ponieważ są one nie tylko (prawie) niemożliwe wyśmiewać / fałszywe, są one tylko do odczytu i nie deterministyczny . Każdy test obejmujący funkcję lub metodę, która bezpośrednio wykorzystuje funkcję zegara systemowego, jest bardzo podatny na przejściowe i / lub przypadkowe awarie.

Rzeczywiste opakowanie jest funkcją 1-liniową return DateTime.Now- ale to wszystko, czego potrzebujesz, aby wziąć źle zaprojektowany, niepodlegający testom obiekt i sprawić, że będzie czysty i testowalny. Możesz zastąpić owinięcie fałszywym zegarem i ustawić jego czas na cokolwiek chcesz. Problem rozwiązany.

Aaronaught
źródło
2

Jak powiedział Doval, ten przykład prawdopodobnie po prostu próbuje zapoznać cię z funkcjami. Ale ogólnie jestem przydatny. Zwłaszcza poprzez podanie niektórych, ale nie wszystkich argumentów, i przekazanie pozostałych, możesz stworzyć funkcję bardziej specyficzną dla wielkości liter z funkcji bardziej ogólnej. Jako trywialny przykład rozważ funkcję sortowania, która zajmuje tablicę do sortowania i funkcję komparatora do sortowania. Określając funkcję komparatora, która porównuje wartość liczbową, mogę utworzyć funkcję sortByNumericalValue, a wywołania tej funkcji są znacznie bardziej jasne i zwięzłe.

raptortech97
źródło
2

Myślenie, które kryje się za twoim pytaniem, wydaje się następujące: „Dlaczego po prostu nie pisać alert("Hello");bezpośrednio? To całkiem proste”.

Odpowiedź brzmi po części dlatego, że tak naprawdę nie chcesz dzwonić alert("Hello")- po prostu chcesz się przywitać.

Lub: Po co przechowywać kontakty w telefonie, skoro można po prostu wybierać numery telefonów? Ponieważ nie chcesz pamiętać wszystkich tych liczb; ponieważ wybieranie numerów jest uciążliwe i podatne na błędy; ponieważ liczba może się zmienić, ale nadal jest to ta sama osoba na drugim końcu. Ponieważ chcesz dzwonić do ludzi , a nie do numerów.

To właśnie rozumieją terminy takie jak abstrakcja, pośrednictwo, „ukrywanie szczegółów implementacji”, a nawet „ekspresyjny kod”.

To samo rozumowanie dotyczy używania stałych zamiast pisania surowych wartości wszędzie. Ty mógł tylko pisać 3.141592...za każdym razem trzeba gatunku, ale na szczęście nie ma Math.PI.

Możemy również spojrzeć na alert()siebie. Kogo obchodzi, w jaki sposób konstruuje i wyświetla to okno dialogowe z ostrzeżeniem? Chcesz tylko ostrzec użytkownika. Pomiędzy pisaniem alert("Hello")a zmianą pikseli na ekranie jest głęboki, głęboki stos kodu i sprzętu, przy czym każda warstwa mówi następnej, czego chce, a następna warstwa zajmuje się szczegółami, dopóki najgłębsza możliwa warstwa nie zmieni niektórych bitów w pamięć wideo.

Naprawdę nie chcesz tego robić samemu, żeby się przywitać.

Programowanie - cóż, każde zadanie, naprawdę - polega wyłącznie na dzieleniu złożonych problemów na porcje do zarządzania. Rozwiązanie każdej części daje ci klocek, a przy wystarczającej liczbie prostych klocków możesz budować duże rzeczy.

To jest algebra.

Flambino
źródło
-1

Dobrym pomysłem jest oddzielenie obliczania wartości (wyrażeń) od wykonywania akcji (instrukcji). Chcemy precyzyjnej kontroli nad tym, gdzie i kiedy zostaną podjęte działania (takie jak wyświetlanie komunikatów), ale przy obliczaniu wartości wolelibyśmy pracować na bardziej abstrakcyjnym poziomie i nie musielibyśmy się martwić, w jaki sposób te wartości są obliczane.

Funkcja, która oblicza tylko wartość zwracaną, używając tylko podanych argumentów, nazywa się czysta .

„Funkcja”, która wykonuje akcję, jest w rzeczywistości procedurą , która ma efekt .

Wszelkie efekty powstałe podczas obliczania wartości nazywane są efektami ubocznymi i lepiej jest ich unikać, gdy to możliwe („Potrzebowałem tylko tego ciągu, nie wiedziałem, że spowoduje to uszkodzenie bazy danych!”).

Aby zminimalizować ryzyko wystąpienia efektów ubocznych, powinniśmy unikać przesyłania zbyt dużej ilości danych do naszych procedur lub dokonywania w nich jakichkolwiek obliczeń; jeśli trzeba wcześniej wykonać pewne obliczenia, zwykle lepiej jest to zrobić osobno w czystej funkcji, a następnie przekazać do procedury tylko wymagany wynik. Dzięki temu cel procedury jest jasny i zmniejsza szansę, że zostanie ona ponownie wykorzystana później w ramach obliczeń (czysta funkcja może zostać ponownie użyta).

Z tego samego powodu powinniśmy unikać przetwarzania wyników w ramach procedury. Lepiej zwrócić wynik (jeśli w ogóle) naszą akcję i wykonać dowolne dalsze przetwarzanie przy użyciu czystych funkcji.

Jeśli będziemy przestrzegać tych zasad, możemy skończyć z procedurą podobną sayHello, która nie potrzebuje żadnych danych i nie daje rezultatu. Dlatego najlepszym interfejsem jest brak argumentów i nie zwracanie wartości. Jest to lepsze niż na przykład wywołanie „console.log” w trakcie niektórych obliczeń.

Aby zmniejszyć potrzebę efektów podczas obliczeń, możemy mieć obliczenia, które zwracają procedury ; na przykład. jeśli musimy zdecydować o działaniu, które możemy podjąć, możemy mieć czystą funkcję, wybrać procedurę i zwrócić ją, zamiast wykonywać ją bezpośrednio.

Podobnie, aby zmniejszyć potrzebę obliczeń podczas procedur, możemy mieć procedury przyjmujące inne procedury jako parametry (być może wynik funkcji); na przykład. biorąc szereg procedur i uruchamiając jeden po drugim.

Warbo
źródło
Wydaje się, że opowiadasz się przeciwko OOP tutaj, gdzie podstawową zasadą jest „mów, nie pytaj”. Podążając za wskazówkami, jest to, co zwykle prowadzi do kodu pełnego bezużytecznych wywołań „getFoo”, „setFoo” i „execute”. OOP polega na łączeniu danych i zachowania, a nie ich rozdzielaniu.
Aaronaught
@Aaronaught To prawda, że ​​jestem przeciwny OOP, ale z pewnością nie polecałbym setFoopołączeń, ponieważ oznacza to zmienne dane. Zmiana zawartości zmiennych / właściwości jest efektem, który powoduje, że wszystkie obliczenia wykorzystujące te dane stają się nieczyste. Nie polecam executepołączeń per se, ale byłoby zalecane runFooprocedury wykonywania czynności na podstawie ich argumentów.
Warbo
-2

Powiedziałbym, że jest to kombinacja odpowiedzi udzielonych przez @MichaelT i @Sleiman Jneidi.

Być może w kodzie jest wiele miejsc, w których chcesz przywitać użytkownika wiadomością Hello, więc spakuj ten pomysł w metodzie. W metodzie możesz ją przetłumaczyć lub rozwinąć na zwykłym „Cześć”, wyświetlając dłuższy tekst. Możesz także skorzystać z miłego okna dialogowego JQuery zamiast alertu.

Chodzi o to, że wdrożenie jest w jednym miejscu. Musisz tylko zmienić kod w jednym miejscu. (SayHello () jest być może zbyt prostym przykładem)

Paweł
źródło
@downvoter (s) - staraj się dzielić swoją wiedzą z resztą z nas. Czy mój post jest po prostu zły, źle napisany czy co?
paul