Створення вкладених функцій з чисто естетичних причин?


16

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

Скажімо , у мене є функція , яка обробляє шматок даних: Function ProcessBigData. Скажімо , мені потрібно кілька кроків процесу, дійсні тільки для цих даних: Step1, Step2, Step3.

Звичайний підхід, який я бачу найбільше у вихідному коді, - це писати такі коментарі:

Function ProcessBigData:
    # Does Step1
    Step1..
    Step1..

    #Does Step2
    Step2..
    Step2..

Що я зазвичай роблю, але завжди відчував себе неправильно через відсутність такого стилю кодування з боку однолітків:

Function ProcessBigData:
    Function Step1:
        Step1..
        Step1..

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

Мене в основному хвилює, чи є якісь недоліки для такого стилю в Javascript та Python

Чи є альтернативи, яких я не бачу?


3
Я нічого не можу сказати про Python, але для Javascript існує вартість продуктивності вкладених функцій: більшість двигунів JavaScript використовують структуру, схожу на список, для представлення змінної області. Додавання додаткового рівня функцій таким чином змушує двигун, можливо, шукати більш довгу / більшу структуру даних при вирішенні змінних. З іншого боку, корінь усього зла - це, звичайно, передчасна оптимізація. :)
Марко

Відповіді:


4

Це не так дивно, як можна подумати. Наприклад, у Standard ML зазвичай прийнято обмежувати сферу допоміжних функцій. Звичайно, у SML є синтаксис для полегшення:

local
    fun recursion_helper (iteration_variable, accumulator) =
        ... (* implementation goes here *)
in
    fun recursive_function (arg) = recursion_helper(arg, 0);
end

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

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


11

Зазвичай це добре робити, коли це можливо, але мені подобається думати про таку роботу не як про "кроки", а як про підзадачі .

Підзадача - це конкретна одиниця роботи, яку можна виконати: вона несе певну відповідальність і визначає вхід (и) та вихід (и) (подумайте над «S» у SOLID ). Підзадача не потребує повторного використання: деякі люди схильні думати: "Мені ніколи не доведеться називати це з чого-небудь іншого, чому б це писати як функцію?" але це помилка.

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

Читабельність.

200+ рядків процедурного коду (частина функції) важко читати. 2-20 лінійних функцій легко читати. Код для людей.

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

Обмежити змінну область застосування

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

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

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

Одиничні тести

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

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

Робота над командою / Дизайн зверху вниз

Підзадачі можуть писати різні люди, незалежно, якщо потрібно.

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

Повторне використання коду

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

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

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


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

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