Управління залежністю є великою проблемою в ООП з наступних двох причин:
- Щільне з'єднання даних і коду.
- Повсюдне застосування побічних ефектів.
Більшість програмістів OO вважають тісний зв'язок даних та коду повністю корисним, але це пов'язано з вартістю. Управління потоком даних через шари - неминуча частина програмування в будь-якій парадигмі. З'єднання ваших даних і коду додає додаткову проблему, що якщо ви хочете використовувати функцію в певний момент, вам доведеться знайти спосіб отримати її об'єкт до цієї точки.
Використання побічних ефектів створює подібні труднощі. Якщо ви використовуєте побічний ефект для певної функціональності, але хочете мати можливість замінити його реалізацію, у вас майже немає іншого вибору, окрім як ввести цю залежність.
Розглянемо як приклад спамерську програму, яка скреблі веб-сторінки на адреси електронної пошти, а потім надсилає їх по електронній пошті. Якщо у вас є думка про DI, зараз ви думаєте про сервіси, які ви будете інкапсулювати за інтерфейсами, і які служби куди вводять. Я залишу цей дизайн як вправу для читача. Якщо у вас FP-мислення, зараз ви думаєте про входи та виходи для найнижчого рівня функцій, наприклад:
- Введіть адресу веб-сторінки, виведіть текст цієї сторінки.
- Введіть текст сторінки, виведіть список посилань на цій сторінці.
- Введіть текст сторінки, виведіть список адрес електронної пошти на цій сторінці.
- Введіть список адрес електронної пошти, виведіть список адрес електронної пошти із видаленими дублікатами.
- Введіть електронну адресу, виведіть спам для цієї адреси.
- Введіть спам-повідомлення, виведіть команди SMTP, щоб надіслати це повідомлення.
Якщо ви думаєте з точки зору входів та виходів, функціональних залежностей немає, лише залежність від даних. Ось що робить їх таким простим в одиничному тесті. Наступний шар вгору організовує виведення однієї функції, яка буде подана на вхід наступної, і може легко поміняти місцями різні реалізації, якщо потрібно.
В реальному сенсі функціональне програмування, природно, пропонує вам завжди інвертувати свої функціональні залежності, і тому вам зазвичай не потрібно вживати ніяких спеціальних заходів для цього після факту. Коли ви це робите, такі інструменти, як функції вищого порядку, закриття та часткове застосування, полегшують роботу з меншою кількістю котельного шаблону.
Зауважте, що не самі залежності є проблематичними. Це залежності, які вказують неправильний шлях. Наступний шар вгору може мати таку функцію, як:
processText = spamToSMTP . emailAddressToSpam . removeEmailDups . textToEmailAddresses
Цілком нормально, щоб у цього шару були такі жорстко закодовані залежності, оскільки його єдиною метою є склеювання функцій нижнього шару разом. Заміна реалізації так само проста, як і створення іншої композиції:
processTextFancy = spamToSMTP . emailAddressToFancySpam . removeEmailDups . textToEmailAddresses
Ця проста рекомпозиція стає можливою через відсутність побічних ефектів. Функції нижнього шару абсолютно незалежні одна від одної. Наступний шар вгору може вибрати, який processText
насправді використовується на основі конфігурації користувача:
actuallyUsedProcessText = if (config == "Fancy") then processTextFancy else processText
Знову ж таки, не проблема, тому що всі залежності вказують в один бік. Нам не потрібно обертати деякі залежності, щоб усі вони вказували однаково, оскільки чисті функції вже змусили нас це робити.
Зауважте, що ви могли б зробити це набагато більш сполученим шляхом, переходячи config
до нижнього шару, а не перевіряючи його вгорі. FP не заважає вам це робити, але, як правило, робить це набагато більше дратує.