Передача функцій в інші функції як параметри, погана практика?


40

Ми змінювали те, як наша програма AS3 розмовляє з нами, і ми впроваджуємо систему REST, щоб замінити стару.

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

Це дає мені таке "погане почуття", коли я відчуваю, що це жахлива практика, і я можу придумати деякі причини, але я хочу певне підтвердження, перш ніж запропонувати переробляти систему. Мені було цікаво, чи має хто-небудь досвід з цією можливою проблемою?


22
Чому ти так почуваєшся? Чи маєте ви досвід роботи з функціональним програмуванням? (Я припускаю, що це не так, але варто поглянути)
Фоші

8
Тоді вважайте це уроком! Те, що викладає наукові установи та що є корисним, часто на диво мало перетинається. Існує цілий світ методик програмування, які не відповідають такій догмі.
Phoshi

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

36
Ще в той день ми називали ці передачі через функції "зворотні дзвінки"
Майк

Відповіді:


84

Це не проблема.

Це відома методика. Це функції вищого порядку (функції, які приймають функції як параметри).

Цей вид функції також є основним складовим елементом у функціональному програмуванні та широко використовується у функціональних мовах, таких як Haskell .

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


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


8
@BlueHat ви оголосите щось приватним, якщо хочете контролювати його використання, якщо це використання як зворотний виклик для певних функцій, то це нормально
храповик виродка

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

2
Варто зазначити, що якщо ви описуєтесь написання конкретних класів з функціями як конструкторські параметри, ви, ймовірно, робите це неправильно tm.
Гусдор

30

Вони не просто використовуються для функціонального програмування. Вони також можуть бути відомі як зворотні дзвінки :

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

Подумайте про асинхронний код на секунду. Ви передаєте функцію, яка, наприклад, надсилає дані користувачеві. Тільки після завершення коду ви викликаєте цю функцію з результатом відповіді, яку потім функція використовує для повернення даних користувачеві. Це зміна мислення.

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


1
+1 Для використання термінології зворотного дзвінка. Я зустрічаю цей термін набагато частіше, ніж "функції вищого порядку".
Родні Шулер

Мені подобається ця відповідь, тому що ОП, здавалося, описує зворотні виклики, зокрема, а не просто функції вищого порядку загалом: "Наприклад, наш клас, який здійснює виклик на наші сервери, приймає функцію, яку він буде потім викликати та передавати об'єкт до того, коли процес завершений і помилки будуть оброблені тощо. "
Ajedi32

11

Це не погано. Насправді це дуже гарна річ.

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

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

Технічно функція C ++ - це також тип об'єкта зворотного виклику, який використовує синтаксичний цукор operator()(), щоб зробити те ж саме.

Нарешті, є функціональні вказівники у стилі С, які слід використовувати лише на C. Я не буду вникати в деталі, я лише згадую їх для повноти. Інші згадані вище абстракції набагато вищі і повинні використовуватися мовами, які їх мають.

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


Також в C # делегати та лямбда обидва використовуються широко.
Злий собачий пиріг

6

Як вже було сказано, це не погана практика. Це просто спосіб розв’язування та розділення відповідальності. Наприклад, в OOP ви зробите щось подібне:

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

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


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

Так, я просто хотів показати, що це не рідкість.
Філіп Меррі

3

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

Однак є кілька можливих недоліків простих зворотних викликів:

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

З простими веб-сервісами добре працює, як ви це робите, але це стає незручно, якщо вам потрібно більш складні послідовності викликів. Однак є кілька альтернатив. Наприклад, у JavaScript відбувся перехід до використання обіцянок ( Що так чудово в обіцянках JavaScript ).

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


Я також додам, що непотрібне використання зворотних дзвінків може ускладнити стеження за потоком виконання для тих, хто читає код. Явний виклик легше зрозуміти, ніж зворотний виклик, встановлений у віддаленій частині коду. (Точки перерви на допомогу!) Тим не менш, саме так події починаються в браузері та інших системах, що базуються на подіях; тоді нам потрібно зрозуміти стратегію випромінювача подій, щоб знати, коли і в якому порядку будуть викликатися обробники подій.
joeytwiddle
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.