Це хороша закономірність: заміна тривалої функції рядом лямбда?


14

Нещодавно я натрапив на таку ситуацію.

class A{
public:
    void calculate(T inputs);
}

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

  • запишіть це як стінку тексту - переваги - вся інформація знаходиться в одному місці
  • писати privateфункції класу в класі та використовувати їх у calculateтілі тіла - недоліки - решта класу не знає / не піклується / не розуміє про ці методи
  • написати calculateтак:

    void A::calculate(T inputs){    
        auto lambda1 = () [] {};    
        auto lambda2 = () [] {};    
        auto lambda3 = () [] {};
    
        lambda1(inputs.first_logical_chunk);
        lambda2(inputs.second_logical_chunk);
        lambda3(inputs.third_logical_chunk);
    }

Чи можна це вважати хорошою чи поганою практикою? Чи виявляє такий підхід якісь проблеми? Загалом, чи слід вважати це добрим підходом, коли я знову стикаюся з тією ж ситуацією?


Редагувати:

class A{
    ...
public:
    // Reconfiguration of the algorithm.
    void set_colour(double colour);
    void set_density(double density);
    void set_predelay(unsigned long microseconds);
    void set_reverb_time(double reverb_time, double room_size);
    void set_drywet(double left, double right);
    void set_room_size(double value);;

private:
    // Sub-model objects.
    ...
}

Усі ці методи:

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

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

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


2
Як ви думаєте, що завдяки використанню лямбдаз ви отримаєте те, що виклики функцій не будуть?
Blrfl

1
Firstly, A represents an object in the physical world, which is a strong argument for not splitting the class up.Напевно Aпредставляє дані про об'єкт, який міг би існувати у фізичному світі. Ви можете мати екземпляр Aбез реального об'єкта і реального об'єкта без екземпляра A, тому поводитися з ними так, як вони одне й те саме, є безглуздим.
Довал

@Blrfl, інкапсуляція - ніхто calculate()не знатиме про ці підфункції.
Ворак

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

1
"По-перше, Aявляє собою об'єкт у фізичному світі, що є сильним аргументом для нерозбиття класу на". Про це мені, на жаль, сказали, коли я почав програмувати. Мені знадобилися роки, щоб зрозуміти, що це купа хокейного коня. Це жахлива причина групувати речі. Я не можу сформулювати, які є вагомі причини для групування речей (принаймні, на моє задоволення), але це одна, яку ви повинні відкинути зараз. Зрештою, будьте всім "хорошим кодом", це те, що він працює правильно, досить легко зрозуміти і змінити відносно легко (тобто зміни не мають дивних побічних ефектів).
jpmc26

Відповіді:


13

Ні, це, як правило, не вдалий зразок

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

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

  • Закриття Можна використовувати змінні у зовнішній області всередині лямбда. Це дуже потужно і дуже складно.
  • Перепризначення. Хоча ваш приклад ускладнює виконання завдання, завжди потрібно звертати увагу на ідею, що код може змінювати функції в будь-який час.
  • Першокласні функції. Ви можете передати лямбда-функцію іншій функції, виконуючи щось відоме як "функціональне програмування".

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

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

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

  • Функції, розміщені локально в потоці вихідного коду, будуть виконуватись так само добре, не викликаючи лямбда. Знову ж таки, все важливо - читач може це прочитати.
  • Коментарі у верхній частині функції говорять "ми ділимо цю функцію на три частини", а потім великі довгі рядки // ------------------між кожною частиною.
  • Ви також можете помістити кожну частину розрахунку у власну сферу. Це бонус негайно довести, що не викликає сумнівів, що не існує змінної розподілу між частинами.

РЕДАКТУВАННЯ: Побачивши вашу редагування з прикладом коду, я схиляюся до того, що нотація коментарів є найчистішою, з дужками для забезпечення меж, які пропонують коментарі. Однак якщо будь-який з функцій повторно використовувався в інших функціях, я рекомендую замість цього використовувати функції

void A::set_room_size(double value)
{
    {
        // Part 1: {description of part 1}
        ...
    }
    // ------------------------
    {
        // Part 2: {description of part 2}
        ...
    }
    // ------------------------
    {
        // Part 3: {description of part 3}
        ...
    }
}

Тож це не об'єктивне число, але я суб'єктивно стверджую, що сама наявність лямбда-функцій змушує мене звертати приблизно в 10 разів більше уваги на кожен рядок коду, тому що вони мають стільки потенціалу, щоб отримати небезпечний складний, швидкий та інше невинно виглядає рядок коду (на кшталт count++)
Cort Ammon

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

"Хоча, якщо бачити, що лямбди не захоплюють, читач не буде обтяжений силою лямбд". Це насправді справедлива, але суперечлива заява, яка вражає серце лінгвістики. Слова зазвичай мають конотації, що виходять за межі їх позначень. Незалежно від того lambda, чи є моя конотація несправедливою, або якщо ви грубо змушуєте людей дотримуватися суворого позначення, відповідь непроста. Насправді для вас може бути цілком прийнятно використовувати lambdaтакий спосіб у вашій компанії, і зовсім неприйнятно для моєї компанії, і жоден з них насправді не повинен помилятися!
Корт Аммон

Моя думка про конотацію лямбда випливає з того, що я виріс на C ++ 03, а не на C + 11. Я витрачав роки на розробку вдячності за конкретні місця, де C ++ постраждав від нестачі lambda, наприклад, for_eachфункції. Відповідно, коли я бачу, lambdaщо не відповідає одному з тих проблем, які легко помітити, перше припущення, яке я приходжу, - це те, що воно, ймовірно, буде використовуватися для функціонального програмування, оскільки воно не було необхідне інакше. Для багатьох розробників функціональне програмування - це зовсім інший спосіб мислення, ніж процедурне або OO програмування.
Корт Аммон

Дякуємо за пояснення. Я початківець програміст і зараз радий, що запитав - до того, як звичка народилася.
Vorac

20

Я думаю, ти зробив неправильне припущення:

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

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

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

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

Те, що ви написали, трохи нагадує вкладені об’єкти функцій у стилі Javascript. Це знову ж таки ознака того, що вони тісно належать разом. Ви впевнені, що вам не слід складати для них окремий клас?

Підводячи підсумок, я не думаю, що це гарна модель.

ОНОВЛЕННЯ

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


Звучить розумно, але мені важко уявити реалізацію. Клас файлового діапазону зі статичними методами? Клас, визначений всередині A(можливо, навіть функтор з усіма іншими методами приватними)? Клас оголошений і визначений всередині calculate()(це дуже схоже на мій приклад лямбда). Як уточнення, calculate()це один із сімейства методів ( calculate_1(), calculate_2()тощо), з яких всі прості, лише цей - 2 екрани формул.
Vorac

@Vorac: Чому calculate()настільки довше, ніж у всіх інших методів?
Кевін

@Vorac Насправді важко допомогти, не побачивши свій код. Чи можете ви також розмістити його?
Gábor Angyal

@Kevin, формули для всього наведені в вимогах. Код розміщено.
Ворак

4
Я б схвалив це 100 разів, якби міг. "Моделювання об'єктів у реальному світі" - це початок смертної спіралі Object Design. Це гігантський червоний прапор у будь-якому коді.
Фред Чарівна Чудо-Собака
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.