Яка перевага функції без параметрів, яка викликає лише іншу функцію


21

Підручник (для Javascript), який я роблю, пропонує запропонувати написати таку функцію, як ця:

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

Окрім затуплення, чи є користь написати щось подібне в реальному житті? Якщо так, то які переваги?


14
Це, мабуть, просто намагається показати вам, як визначити власні функції.
Довал

4
Я б ніколи не писав код таким чином, щоб придушити його - код для програміста легко читати, а не навпаки. Використовуйте обфускатор для обфускації.
реге

32
Перевага полягає в тому, що він обертає функцію з параметрами. Таким чином, якщо згодом ви хочете зробити свою програму іспанською, вам потрібно лише змінити "Привіт" на "Ола" в одному місці.
Пітер Б

9
@PieterB насправді, "Hola"
rev

29
@PieterB - І якщо ви виявите, що ви неправильно написали "Hola", вам потрібно виправити це лише в одному місці.
Даніель Р Хікс

Відповіді:


60

Пробачте про мою пам'ять, якщо у мене це неправильно ... Javascript не є моєю бажаною мовою реалізації.

Є кілька причин, через які можна захотіти, щоб функція no arg обгортала інший виклик функції. Поки простий дзвінок наwindow.alert("Hello"); - це те, що ви могли собі уявити просто замість того, щоб дзвонити безпосередньо замість sayHello().

Але що робити, якщо в цьому є більше? У вас є десяток місць, куди ви хочете зателефонувати, sayHello()і window.alert("Hello");натомість написали . Тепер ви хочете, щоб це зробити window.alert("Hello, it is now " + new Date()). Якщо ви завершили всі ці дзвінки якsayHello() змінивши його на одне місце. Якщо цього не зробили, ви поміняли його в десятку місць. Це стосується не повторювати себе . Ви робите це, тому що не хочете робити це десяток разів у майбутньому.

Раніше я працював з бібліотекою i18n / l10n, яка використовувала функції для локалізації тексту на стороні клієнта. Розглянемо sayHello()функцію. Ви можете його роздрукувати, holaколи користувач локалізований на іспанській мові. Це може виглядати приблизно так:

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

Хоча бібліотека так не працювала. Натомість він мав набір файлів, який виглядав так:

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

І тоді бібліотека виявила б налаштування мови браузера, а потім створить динамічні функції з відповідною локалізацією як зворотне значення для виклику функції без аргументу на основі відповідного файлу локалізації.

Мені недостатньо кодера Javascript, щоб сказати, чи добре це чи погано ... просто, що це було і може розглядатися як можливий підхід.

Справа в тому, що перетворення виклику на іншу функцію у власній функції часто буває досить корисним і допомагає з модуляризацією програми, а також може призвести до більш легкого для читання коду.

Все, що трохи відсторонено, ви працюєте з підручника. Потрібно ввести речі якомога простіше на початку. Введення викликів функції стилю varargs з самого початку може призвести до дуже заплутаного коду для людини, яка взагалі не знайома з кодуванням. Набагато простіше перейти від жодних аргументів, до аргументів, до стилю varargs - з кожною основою на попередніх прикладах та розумінням.


3
window.alertтакож часто використовується як заповнювач заповнення під час розробки, поки не буде спроектовано / реалізовано приємний модуль / спливаюче вікно, тому, як і мовна проблема, його можна вимкнути набагато простіше. Або якщо у вас вже є, оновлення дизайну через кілька років вниз може зажадати аналогічних змін.
Ізката

34

Я думаю, що її корисно іноді для приховування реалізації.

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

І це дає вам можливість змінити її згодом

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

19

Окрім затуплення, чи є користь написати щось подібне в реальному житті? Якщо так, то які переваги?

  • централізація: хоча реалізація є однією лінією, якщо це лінія, яка часто змінюється, ви, мабуть, вважаєте за краще змінити її в одному місці, ніж де б не говорилиЗдрастуйте.

  • мінімізація / приховування залежностей: клієнтському коду більше не потрібно знати, що є об’єкт вікна, і ви навіть можете змінити всю реалізацію, не впливаючи на код клієнта взагалі

  • виконання контракту: клієнтський код може очікувати модуля, який має функцію sayHello. У цьому випадку, навіть якщо тривіальна, функція повинна бути там.

  • послідовність рівнів абстракції: якщо клієнтський код використовує операції високого рівня, вам цікаво писати клієнтський код у термінах "sayHello, sayBye" та деякі інші функції "sayXXX" замість об'єктів вікна. Infact, у коді клієнта ви можете навіть не хотіти знати, що існує такий предмет, як "віконний" об'єкт.


Мені подобається ця відповідь. Часто дизайн є головною причиною цих функцій, особливо дизайн OO, коли ви хочете, щоб об'єкти, відповідальні за поведінку, виконували функції інших об'єктів із специфічною поведінкою. Зображуючи свій автомобіль, точніше перемикаючи передачі. Ви перемикаєте передачу вгору, "кажучи" своїй палиці переключитися вгору. У свою чергу він переадресовує цей дзвінок у вашу коробку передач. Але крім того, він спочатку перевіряє, що ви можете перейти зі свого поточного становища.
Ерік

3
Інші причини хороші, але +1 для згадування рівнів абстракції. Використання чітких абстракцій та назв хороших функцій може значно полегшити читання та підтримку старого коду. У тому випадку, коли виклик sayHello (), ви не обов'язково переймаєтесь тим, як це реалізовано, ви просто хочете зрозуміти, у чому полягає мета цього рядка коду. І назва функції, як sayHello (), робить це очевидним.
kkrambo

Також +1 для згадування рівнів абстракції: може статися, що реалізація функції на одному рівні абстракції складається з одного виклику до іншої функції нижчого рівня абстракції. І що?
Джорджіо

7

Дивно, що жодна інша людина не згадала про тестування.

Конкретна "загорнута" лінія, яку ви вибрали window.alert('hello'), - насправді є прекрасним прикладом цього. Все, що стосується windowоб'єкта, перевірити справді, дуже болісно. Помножте це у своїй програмі на 1000 разів, і я гарантую, що розробники зрештою відмовляться від тестування. З іншого боку, досить просто пограбувати sayHelloфункцію шпигуном і перевірити, що його викликали.

Більш практичний приклад - адже насправді, хто насправді використовує window.alert(...)у виробничому коді? - перевіряє системний годинник. Так, наприклад, це буде обгортання DateTime.Nowв .NET, time(...)в C / C ++ або System.currentTimeMillis()в Java. Ви дійсно хочете загорнути тих у залежність, яку ви можете ввести, оскільки вони не лише (майже) неможливо знущатися / підробляти, вони лише для читання та недетерміновані . Будь-який тест, що охоплює функцію або метод, який безпосередньо використовує функцію системного годинника, є надзвичайно ймовірним, що зазнає переривчасті та / або випадкові збої.

Фактична обгортка - це функція 1-рядкової лінії - return DateTime.Now- але це все, що потрібно для того, щоб взяти недобре розроблений, не перевіряється об'єкт і зробити його чистим і перевіреним. Ви можете замінити фальшивий годинник для обгортки і встановити його час на все, що завгодно. Проблема вирішена.


2

Як сказав Доваль, цей приклад, мабуть, просто намагається познайомити вас з функціями. Я взагалі, хоча, це корисно. Особливо, вказуючи деякі аргументи, але не всі, і передаючи інші, можна зробити більш загальну для конкретної справи функцію з більш загальної функції. Як дещо тривіальний приклад розглянемо функцію сортування, яка бере масив для сортування та функцію порівняння для сортування. Зазначаючи функцію порівняння, яка порівнює за числовим значенням, я можу зробити функцію sortByNumericalValue, і виклики до цієї функції є набагато більш чіткими і стислими.


2

Мислення, що стоїть за вашим запитанням, здається: "Чому б просто не написати alert("Hello"); безпосередньо? Це досить просто".

Відповідь, частково, тому що ви не дуже хочете телефонувати alert("Hello")- ви просто хочете сказати привіт.

Або: Навіщо зберігати контакти на своєму телефоні, коли ви можете просто набрати телефонні номери? Тому що ви не хочете запам'ятовувати всі ці числа; через те, що набирати номери, є втомливим і схильним до помилок; бо число може змінитися, але це все одно та сама людина на іншому кінці. Тому що ти хочеш телефонувати людям , а не номерам.

Це те, що розуміється під такими термінами, як абстракція, непрямість, "приховування деталей реалізації" і навіть "виразний код".

Це ж міркування стосується використання констант замість того, щоб скрізь писати необроблені значення. Ви можете просто писати 3.141592...кожен раз, коли вам потрібно π, але, на щастя, є Math.PI.

Ми також можемо подивитися на alert()себе. Кого хвилює, як він створює та відображає це діалогове вікно сповіщення? Ви просто хочете попередити користувача. Між написанням alert("Hello")та зміною пікселів на екрані існує глибокий, глибокий стек коду та обладнання, при цьому кожен шар повідомляє наступне, що він хоче, і наступний шар дбає про деталі, поки найглибший можливий шар не перекине деякі біти в відеопам'ять.

Ви дійсно не хочете робити все, що потрібно, щоб просто привітатися.

Програмування - ну, будь-яке завдання, насправді - це розбиття складних проблем на керовані шматки. Розв’язуючи кожен шматок, ви створюєте будівельний блок, і з досить простих будівельних блоків ви можете будувати великі речі.

Це алгебра.


-1

Недоцільно тримати обчислення значень (виразів) окремо від виконання дій (операторів). Ми хочемо точного контролю над тим, де і коли будуть вживатися дії (наприклад, показ повідомлень), але при обчисленні значень, ми краще працюємо на більш абстрактному рівні і не будемо дбати про те, як ці значення обчислюються.

Функція, яка обчислює лише повернене значення, використовуючи лише наведені аргументи, називається чистою .

"Функція", яка виконує дію, - це фактично процедура , яка має ефект .

Будь-які ефекти, спричинені під час обчислення значення, називаються побічними ефектами , і краще уникати їх там, де це можливо ("Мені просто потрібна ця струна, я не знав, що вона забиває базу даних!").

Щоб мінімізувати ймовірність виникнення побічних ефектів, нам слід уникати надсилання занадто багато даних до наших процедур або внесення в них будь-якого розрахунку; якщо деякий розрахунок потрібно виконати заздалегідь, зазвичай це краще зробити окремо в чистому режимі, а потім передати процедурі лише необхідний результат. Це забезпечує чітку мету процедури та зменшує ймовірність її повторного використання згодом як частина обчислення (замість неї можна повторно використовувати чисту функцію).

З цієї ж причини нам слід уникати обробки результатів всередині процедури. Краще повернути результат (якщо такий є) нашою дією та виконати будь-яку подальшу обробку з чистими функціями.

Якщо ми будемо дотримуватися цих правил, ми можемо закінчити процедуру на зразок sayHello, яка не потребує даних і не має результату. Тому найкращим інтерфейсом для нього є відсутність аргументів і не повернення значення. Це переважно, ніж, наприклад, виклик "console.log" в середині деякого обчислення.

Щоб зменшити потребу в ефектах під час обчислення, ми можемо мати розрахунки, які повертають процедури ; напр. якщо нам потрібно визначитися з дією, яку потрібно здійснити, ми можемо мати чисту функцію вибрати процедуру та повернути її, а не виконувати її безпосередньо.

Так само, щоб зменшити потребу в обчисленні під час процедур, ми можемо мати процедури, які приймають інші параметри як параметри (можливо, результат функції); напр. прийняття масиву процедур і виконання одна за одною.


Ви, здається, виступаєте проти ООП, де основним принципом є "скажи, не питай". Дотримуючись поради, ось що зазвичай призводить до коду, пронизаного непотрібними дзвінками "getFoo", "setFoo" та "Execute". OOP - це поєднання даних та поведінки, а не їх розділення.
Aaronaught

@Aaronaught Це правда, що я проти OOP, але я точно не рекомендую setFooдзвінки, оскільки це передбачає змінні дані. Зміна вмісту змінних / властивостей - це ефект, який спричиняє нечистість усіх обчислень, що використовують ці дані. Я б не рекомендував executeдзвінки як такі, але я рекомендував биrunFoo процедури для виконання дій на основі їх аргументів.
Варбо

-2

Я б сказав, що це поєднання відповідей, наданих @MichaelT та @Sleiman Jneidi.

Можливо, в коді є ряд місць, де ви хочете привітати користувача повідомленням Hello, щоб ви упакували цю ідею методом. У методі ви можете перекласти його або розгорнути на простий "Привіт", відображаючи довший текст. Також ви можете використовувати гарне діалогове вікно JQuery замість попередження.

Справа в тому, що реалізація знаходиться в одному місці. Вам просто потрібно змінити код в одному місці. (SayHello () - це, мабуть, занадто простий приклад)


@downvoter (s) - дбайте поділитися своїми знаннями з іншими нами. Чи мій пост просто неправильний, погано написаний чи що?
Павло
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.