Чи замінює функціональне програмування шаблони дизайну GoF?


1047

Оскільки я почав вивчати F # та OCaml минулого року, я прочитав величезну кількість статей, які наполягають на тому, що шаблони дизайну (особливо на Java) є вирішенням для відсутніх функцій в обов'язкових мовах. Одна із знайдених нами статей висловлює досить сильну заяву :

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

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

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

Крім того, у функціональних мовах, які підтримують OOP (наприклад, F # та OCaml), мені здається очевидним, що програмісти, які використовують ці мови, будуть використовувати ті самі шаблони дизайну, які є у всіх інших мовах OOP. Насправді зараз я використовую F # та OCaml щодня, і між шаблонами, якими я користуюсь у цих мовах, немає чітких відмінностей порівняно з моделями, якими я користуюся, коли пишу на Java.

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


18
Ви можете подивитися статтю Стіва Йегге ( steve-yegge.blogspot.com/2006/03/… )
Ральф

27
"книга є агностичною мовою, і моделі стосуються інженерії програмного забезпечення взагалі" - слід зазначити, що книга не погоджується з цим твердженням, в тому сенсі, що деяким мовам не потрібно виражати певні речі, такі як шаблони дизайну: "Наші зразки припустимо, що є мовні функції на рівні Smalltalk / C ++, і цей вибір визначає, що можна, а що не можна легко реалізувати [...] Наприклад, у CLOS є багатометодні методи, які зменшують потребу в такому шаблоні, як Visitor (Сторінка 331). " (сторінка 4)
Гільденстерн

6
Також пам’ятайте, що багато моделей дизайну навіть не потрібні на достатньо високому рівні імперативних мов.
Р. Барцелл

3
@ R.Barzell Що це за "достатньо високі імперативні мови"? Дякую.
cibercitizen1

5
@ cibercitizen1 мови, що набираються качками, з підтримкою функцій вищого порядку та анонімними функціями. Ці функції забезпечують велику частину енергії, яку мав надати багато моделей дизайну.
Р. Барцелл

Відповіді:


1076

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

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

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

Ось що Банда четвірок має сказати з цього приводу:

Вибір мови програмування важливий, оскільки він впливає на точку зору. Наші зразки передбачають мовні особливості рівня Smalltalk / C ++, і цей вибір визначає, що можна, а що не можна легко реалізувати. Якби ми припускали процедурні мови, ми могли б включити шаблони дизайну під назвою "Спадщина", "Інкапсуляція" та "Поліморфізм". Так само деякі наші шаблони підтримуються безпосередньо менш поширеними об'єктно-орієнтованими мовами. У CLOS є, наприклад, багато методи, які зменшують потребу в такому шаблоні, як Visitor. Насправді, між Smalltalk та C ++ є достатньо відмінностей, щоб означати, що деякі зразки можна виразити легше на одній мові, ніж на іншій. (Див., Наприклад, Ітератор.)

(Наведене вище - цитата з Вступу до книги "Шаблони дизайну", сторінка 4, параграф 3)

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

Який шаблон команди, якщо не наближення функцій першого класу? :) На мові FP ви просто передаєте функцію як аргумент іншій функції. У мові OOP вам потрібно перенести функцію в клас, який ви можете інстанціювати, а потім передавати цей об'єкт іншій функції. Ефект той самий, але в OOP це називається шаблон дизайну, і він вимагає набагато більше коду. А яка абстрактна фабрична закономірність, якщо не каррінг? Передайте параметри функції за раз, щоб налаштувати, яке значення вона випльовує, коли ви нарешті викликаєте її.

Так, так, кілька мод дизайну GoF виглядають надлишковими в мовах FP, оскільки існують більш потужні та прості у використанні альтернативи.

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

І це працює обома способами. Як я вже сказав, у ПП є також свої дизайнерські зразки; люди просто не вважають їх такими.

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

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

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

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

Крім того, у функціональних мовах, які підтримують OOP (наприклад, F # та OCaml), мені здається очевидним, що програмісти, які використовують ці мови, будуть використовувати ті самі шаблони дизайну, які є у всіх інших мовах OOP. Насправді зараз я використовую F # та OCaml щодня, і між шаблонами, якими я користуюсь у цих мовах, та зразками, якими я користуюся, коли я пишу на Java, немає різних відмінностей.

Можливо тому, що ти все ще думаєш імперативно? Багатьом людям, все життя стикаючись з імперативними мовами, важко відмовитися від цієї звички, коли вони спробують функціональну мову. (Я бачив декілька кумедних спроб у F #, де буквально кожна функція являла собою лише рядок висловлювань "нехай", в основному так, ніби ви взяли програму C і замінили всі крапки з комою на "хай". :))

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

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

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

Так. :) Коли ви працюєте мовою FP, вам більше не потрібні специфічні шаблони дизайну OOP. Але вам все одно потрібні деякі загальні шаблони дизайну, наприклад, MVC або інші речі, що не стосуються OOP, і замість цього вам потрібна пара нових «дизайнерських моделей», характерних для FP. У всіх мов є свої недоліки, і дизайнерські зразки - це зазвичай те, як ми працюємо навколо них.

У будь-якому випадку, вам може бути цікаво спробувати свої сили на «чистіших» мовах FP, наприклад, ML (мій особистий фаворит, принаймні для навчальних цілей) або Haskell , де у вас немає милиці OOP, щоб відкинутися, коли ви зіткнулися з чимось новим.


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

Як уже було сказано, більшість моделей дизайну притаманні одній парадигмі програмування, а іноді навіть одній конкретній мові. Часто вони вирішують проблеми, які існують лише в цій парадигмі (див. Монади для ПП або абстрактні заводи для ООП).

Чому абстрактний заводський зразок не існує у FP? Тому що проблеми, яку вона намагається вирішити, не існує.

Отже, якщо існує проблема в мовах OOP, яких немає в мовах FP, то очевидно, що це є недоліком мов OOP. Проблему можна вирішити, але ваша мова цього не робить, але вимагає від вас купіння кодового коду, щоб обійти її. В ідеалі, ми б хотіли, щоб наша мова програмування магічно усунула всі проблеми. Будь-яка проблема, яка все ще є, в принципі є недоліком мови. ;)


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

135
С.Лотт: Вони описують рішення проблем, які існують в даній мові, так. У мовах FP відсутній шаблон дизайну Command, оскільки проблема, яку він намагається вирішити, не існує. Це означає, що вони вирішують проблеми, які сама мова не може вирішити. Тобто недоліки в мові
jalf

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

41
Звичайно, монади - це математичне поняття, але вони також є зразком. "Модель FP" монад дещо відрізняється від математичної концепції монад. Перший - це шаблон, який використовується для подолання певних "обмежень" у чистих мовах ПП. Останнє є універсальним математичним поняттям.
jalf

69
Зверніть увагу, що монади в Haskell використовуються для інших речей, ніж стан, що змінюється, наприклад, для винятку, продовження, розуміння списків, розбору, асинхронного програмування тощо. Але все це застосування монад, напевно, можна було б назвати моделями.
ЖакБ

152

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

Функціональне програмування не те саме, що об'єктно-орієнтоване програмування. Об'єктно-орієнтовані структури дизайну не стосуються функціонального програмування. Натомість у вас є функціональні схеми програмування.

Для функціонального програмування ви не будете читати книги шаблонів дизайну ОО; ви читатимете інші книги про схеми дизайну FP.

мовний агностик

Не зовсім. Тільки мова-агностик щодо мов ОО. Шаблони дизайну взагалі не стосуються процедурних мов. Вони майже не мають сенсу в контексті дизайну реляційних баз даних. Вони не застосовуються при розробці електронної таблиці.

типова модель дизайну OOP та її функціональний еквівалент?

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

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

Функціональне програмування (як і всі стилі програмування) має схеми дизайну. Реляційні бази даних мають схеми дизайну, OO має схеми дизайну, а процедурне програмування має схеми дизайну. Все має дизайнерські зразки, навіть архітектура будівель.

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

Кожен, хто думає про те, що вони роблять, розкриє схеми дизайну.


12
MVC не є дизайном OO. Це архітектурний дизайн - така модель застосовується досить широко.
S.Lott

1
@Princess: функціональне програмування не обов'язково простіше. У вашому прикладі, так. З іншого боку, присяжних все ще немає. Але ви відкинули шаблон дизайну Java OO і прийняли шаблон дизайну ПП.
С.Лотт

1
+1: Я вважаю за краще цю відповідь, ніж відповідь Джальфа вище. Хоча деякі схеми дизайну вирішують недоліки в мові, але не всі. Наприклад, я навряд чи можу сказати, що модель «знімання рекурсивного вузла» вирішує дефіцит мови, це просто корисна ідіома для послаблення залежностей.
Джон Харроп

9
Java 8 буде включати закриття, відомі як анонімні функції, так само лямбда-вирази. Це зробить шаблон дизайну команд застарілим для Java. Це приклад недоліку мови, ні? Вони додали відсутні функції, і тепер вам не потрібна схема дизайну.
Todd Chaffee

2
+1 для заключного речення. Шаблони проектування призначені для спрощення програмування та підвищення результативних програм, які вони роблять ефективнішими.
Сортер

46

Коментар Брайана щодо тісного зв’язку між мовою та зразком суттєвий,

Відсутня частина цього обговорення - концепція ідіоми. Величезний вплив на це мала книга Джеймса О. Коплієна "Розширений C ++". Задовго до того, як він відкрив Крістофера Олександра та Колону без імені (і ти не можеш розважливо говорити про зразки, не читаючи Олександра), він розповів про важливість оволодіння ідіомами для справжнього вивчення мови. В якості прикладу він використовував рядову копію на C. while(*from++ = *to++);Ви можете бачити це як бандаїд для відсутньої функції мови (або функції бібліотеки), але, що насправді важливо, це те, що це більша одиниця думки чи вираження, ніж будь-яка з її частини.

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

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


8
Дякую! ось в чому полягають закономірності - "концептуальне потрохування", щоб знизити когнітивний тягар.
Рандалл Шульц

І функціональні монади безумовно належать до цієї дискусії.
Грег

@RandallSchulz: мовні особливості (і, звичайно, їхнє ідіоматичне використання) також добре впишуться в категорію "концептуального чваннювання для зниження когнітивного тягаря".
Рой Тінкер

39

Книга GoF явно пов'язана з OOP - назва - Шаблони дизайну - Елементи багаторазового об’єктно-орієнтованого програмного забезпечення (моє наголос).



26

Ось ще одне посилання, що обговорює цю тему: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/

У своїй публікації в блозі Едуард описує всі 23 оригінальних зразки GoF з точки зору Haskell.


4
Стаття, схоже, не показує шаблони дизайну в Haskell, але показує, як Haskell вирішує ці потреби без вказаних шаблонів.
Fresheyeball

3
@Fresheyball: залежить від вашого визначення шаблонів. Чи відображення функції над списком є ​​варіантом шаблону відвідувачів? Я взагалі вважав, що відповідь "так". Візерунки повинні виходити за межі певного синтаксису. Застосовувана функція може бути обернена як об'єкт або передана як покажчик функції, але для мене концепція така ж. Ви не згодні?
srm

20

Якщо ви спробуєте поглянути на це на рівні "дизайнерських моделей" (загалом) та "FP проти OOP", відповіді, які ви знайдете, будуть у кращому випадку мутними.

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

Так, наприклад, деякі специфічні шаблони, такі як " Відвідувач" , " Стратегія" , " Команда" та " Обозреватель", безумовно, змінюються або зникають при використанні мови з алгебраїчними типами даних і узгодженням шаблонів , закриттям , функціями першого класу тощо. Деякі інші зразки з книги GoF досі "приклеїтися", хоча.

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

(Убік: ось недавній я написаний блог , у якому є інші посилання на більш детальну дискусію щодо ПП та моделей дизайну.)


Як ви можете сказати, що шаблон відвідувача "зникає"? Хіба це не просто перетворюється з "зробити інтерфейс для відвідувачів з купою методів відвідування" в "використовувати типи об'єднання та відповідність шаблонів"?
Гейб

22
Так, але це змінилося від шаблону, який є дизайнерською ідеєю, про яку ви читаєте в книзі та застосовуєте до свого коду, на "просто код". Тобто, "використовувати типи об'єднання та відповідність шаблонів" - це те, як ви зазвичай кодуєте речі такою мовою. (Аналогія: якщо на жодних мовах не було forциклів, і всі вони просто мали whileцикли, то "For" може бути ітераційним малюнком. Але коли forлише конструкція, що підтримується мовою, і як люди кодують зазвичай, це не шаблон - ви не ' Тобі потрібен зразок, це просто код, людина.)
Брайан,

4
Інакше кажучи, лайммусовий тест, який, можливо, не поганий для "чи це закономірність", це: представити код, написаний таким чином, до студента другого курсу, який спеціалізується в КС з однорічним досвідом програмування на вашій мові. Якщо ви покажете їм код, і вони йдуть "це розумний дизайн", то це шаблон. Якщо ви покажете їм код, і вони йдуть «добре, да!», То це не зразок. (І якщо ви покажете цей "Відвідувач" кожному, хто зробив ML / F # / Haskell протягом року, вони пройдуть "добре, да!")
Брайан

1
Брайан: Я думаю, що ми просто маємо різні визначення поняття "візерунок". Я вважаю , будь-якої ідентифікованої дизайн абстракції бути малюнком , в той час як ви вважаєте , тільки неочевидні абстракції бути малюнком . Тільки тому, що C # має, foreachа Haskell mapMне означає, що вони не мають ітераторського малюнка. Я не бачу жодних проблем у тому, що модель Iterator реалізована як загальний інтерфейс IEnumerable<T>у C # та клас типу Traversableв Haskell.
Гейб

Можливо, не очевидні зразки корисні інженерам-програмістам, але всі моделі корисні дизайнерам мов. Т.е. "Якщо ви створюєте нову мову, обов'язково включіть чистий спосіб виразити шаблон ітератора." Навіть очевидні закономірності цікавлять, коли ми починаємо задавати питання: "Чи є кращий синтаксис для висловлення цієї ідеї?" Зрештою, саме це спонукає когось створити передбачення.
srm

16

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

Я думаю, що зрозуміло, що більшість "креативних" або "структурних" моделей в GoF - це лише хитрощі, щоб змусити системи примітивного типу в Java або C ++ робити те, що ти хочеш. Але решта заслуговує на розгляд незалежно від того, на якій мові ви програмуєте.

Один може бути прототипом; хоча це фундаментальне поняття JavaScript, воно має реалізовуватися з нуля іншими мовами.

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


2
Що дивного аналізу зробити, оскільки моделі GoF були спеціально розроблені для мов OOP на основі класів. Здається, трохи, як аналізувати, чи добре гайкові ключі виконувати електричні роботи.
чудовий

@mulous: Не дуже. Об'єктно-орієнтована забезпечує поліморфізм; функціональне програмування зазвичай забезпечує поліморфізм.
Марцін

@Marcin OO-програміст означає щось дуже інше за поліморфізмом, ніж функціональний програміст.
AndrewC

@AndrewC Я не згоден. Програміст ОО може подумати, що вони означають щось інше, але вони цього не роблять.
Марцін

3
@Marcin На моєму досвіді, програміст OO зазвичай має на увазі підтиповий поліморфізм (часто просто використовуючи Object), використовуючи касти для його досягнення, або спеціальний поліморфізм (перевантаження тощо). Коли функціональний програміст каже, що поліморфізм, він має на увазі параметричний поліморфізм (тобто працює для будь-якого типу даних - Int, функція, список), який, мабуть, більше схожий на загальне програмування OO, ніж це як все, що OO програмісти зазвичай називають поліморфізмом.
AndrewC

15

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


Я повністю загублений. Скласти щось з абстракціями ... Що це означає?
tuinstoel

2
Ви можете створювати доменні абстракції (навіть вбудовані) без макросів. Макроси просто дозволяють вам їх гарненько додавати спеціальний синтаксис.
Джон Харроп

2
Ви можете думати про Lisp як набір Legos для побудови мов програмування - це мова, але це також метамовність. Це означає, що для будь-якого проблемного домену ви можете створити на замовлення мову, яка не має явних недоліків. Це зажадає певної практики, і Курт Гедель може не погодитися, але варто витратити трохи часу з Ліспом, щоб побачити, що воно приносить до столу (підказка, макроси).
Грег

9

І навіть рішення дизайнерських схем OO є мовними.

Шаблони дизайну - це рішення загальних проблем, які ваша мова програмування не вирішує для вас. У Java шаблон Singleton вирішує проблему "що-небудь" (спрощена).

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


8

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


4
GoP-шаблони - це способи вирішення проблеми обмежень певного типу мови програмування, що перешкоджає вам. Наприклад, "Я хочу направити на класи і сказати їм робити об'єкти" -> "Ви не можете, але ви можете зробити об'єкти, схожі на метаклас, що називаються Фабрика". "Я хочу кілька разів" -> "Ви не можете, але є лабіринт, який можна реалізувати під назвою" Шаблон відвідувача ". І т. Д. Жоден із зразків не має сенсу, якщо ви не користуєтесь мовою OOP із певними обмеженнями.
Каз

1
Я не знаю про те, що "жоден" з них має сенс в інших мовах, але я погоджуюся, що багато з них не мають сенсу в інших мовах. Здається, адаптер і міст мають більш багатомовні можливості, трохи зменшуючи кількість відвідувачів і, можливо, трохи менше для слухачів. Однак, зразки на різних мовах завжди будуть страждати від типу "як зробити роботу мови X мовою Y", скорочуючи природні межі мови. Ідеальним прикладом був шаблон Singleton, який, в основному, як я можу отримати глобальні C в OOP? (на що я відповім, ви не повинні).
Едвін Бак

1
Я другий Kaz: Шаблон - це не "спосіб вирішення подібних проблем, які знову і знову бачать", а "спосіб вирішення подібних проблем, які знову і знову бачать І повинні переписуватися знову і знову, тому що мова не дозволяє напишіть це лише один раз ». Іншими словами, якщо мова дозволила витягти / абстрагувати шаблон у бібліотеці / класі / модулі тощо, він перестає бути візерунком, але стає бібліотекою / класом / модулем. У FP набагато простіше витягнути / абстрагувати біти кодів до функції, тому "шаблон" легше перетворюється в код для багаторазового використання, роблячи їх не шаблоном.
mb14

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

1
Повертаючись до цієї розмови через роки, я думаю, що багато людей асоціюють патерни із певною мовою програмування або певною парадигмою програмування. Вони можуть використовуватися в такому контексті, але вони існували до програмування. "Безчасний спосіб побудови" обговорює схеми будівництва архітектури та планування громади. Це означає, що методи, орієнтовані на візерунок, можна використовувати поза "обмеженнями мови", якщо ви не хочете називати будівництво мовою програмування :)
Edwin Buck

8

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

Погляньте на те, як Scala усуває "однотонний візерунок": ви просто оголосите об'єкт замість класу. Ще одна особливість, відповідність шаблону, допомагає уникнути незграбності шаблону відвідувачів. Дивіться порівняння тут: Scala's Pattern Matching = Шаблон відвідувачів на стероїдах

І Scala, як і F #, є синтезом OO-функціоналу. Я не знаю про F #, але, мабуть, має такі особливості.

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

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

for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }

Імперативні мови, такі як Java та C #, прийняли те, що по суті є функціональною конструкцією для вирішення цього питання: "foreach".


Я б сказав, що Scala включає в себе першокласну підтримку одинарного малюнка. Шаблон все ще існує, але код котла, необхідний для нанесення шаблону, значно скорочується порівняно з Java.
ЖакБ

Якщо думки були на зразок *******, ну ... Подивіться на решту відповідей. "ви просто оголосите об'єкт замість класу" так правдиво, я б явно назвав його об'єктом буквально, хоча (тобто var singleton = {};). Мені також подобається згадка про схему передбачення. На жаль, схоже, що більшість людей, які відповіли / коментували це запитання, не розуміють функціонального програмування і скоріше виправдають використання моделей дизайну OOP. +1 для надання конкретних прикладів, я би дав більше, якби міг.
Еван Плейс

@JacquesB Я не можу коментувати Scala / Haskell, але в JavaScript (тобто гібридному функціональному / імперативному) абсолютно немає панелі котлів, ви просто налаштовуєте спосіб оголошення об'єктів, використовуючи комбінації об'єктного буквального синтаксису, анонімних функцій, передаючи функції в якості першого члени класу та допускають багаторазове успадкування (виключаючи потребу в інтерфейсних контрактах).
Еван Плейс

8

Шаблони дизайну GoF - це кодування рецептів для вирішення мов OO, які є нащадками Simula 67 , як-от Java та C ++.

Більшість "неприємностей", які трактуються зразками дизайну, викликані:

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

Немає жодного з цих шаблонів дизайну, який би не зникав у загальній системі Lisp Object, навіть якщо рішення структуровано по суті так само, як у відповідній схемі дизайну. (Більше того, ця об'єктна система передує книзі GoF майже за десятиліття. Загальний Ліс став стандартом ANSI того ж року, коли ця книга була вперше опублікована.)

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

Побудова та немутуючий доступ сумісні з функціональним програмуванням, тому можуть бути застосовані шаблони, пов’язані з обмеженням доступу чи побудови: шаблони, такі як Factory, Facade, Proxy, Decorator та Visitor.

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


2
"Шаблони дизайну GoF - це кодування рецептів рішення" - це просто помилкове твердження.
Джон Петерс

7

Я хотів би підключити пару чудових, але дещо щільних статей Джеремі Гіббонса: "Шаблони дизайну як загальнопрограмні дані для вищого порядку" та "Суть шаблону ітератора" (обидва доступні тут: http: // www. comlab.ox.ac.uk/jeremy.gibbons/publications/ ).

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


6

Ви не можете вести цю дискусію без створення типів систем.

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

Це тому, що ці функції не стосуються тих самих проблем, що і OOP ... вони є альтернативою імперативному програмуванню. Відповідь FP на OOP лежить у типах систем ML та Haskell ... конкретно, суми типів, абстрактних типів даних, модулів ML та класів типів Haskell.

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

Перше, що роблять typeclasses - це усунути потребу в одиночних клавішах.

Ви можете пройти список з 23 та усунути більше, але я зараз не маю часу.


6
Як typeclasses (FP-еквівалент інтерфейсів OOP) усуває потребу в одиночних кнопках (FP-еквівалент глобального стану)?
Гейб

4

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


4
Річ у тому, що головний сенс багатьох моделей полягає у використанні поліморфізму, щоб робити речі, які гідна підтримка концепцій ПС могла б дозволити автоматично. (Більшість втілень, які я бачив, наприклад, у Builder, - це лише напівзміцніння каррі.) Після того, як ви можете легко ставитися до функцій як до значень, шаблони часто спрощуються до точки тривіальності. Вони стають "передавати зворотний виклик" або "мають словник зворотних дзвінків" - і різні класи будівельників, наприклад, можуть не тільки зникати. IMO-модель перестає бути зразком, як тільки це досить тривіально, щоб бути просто тим, як все працює , а не щось, що потрібно реалізувати.
cHao

4

По суті, так !

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

Крім того, на цій сторінці (AreDesignPatternsMissingLanguageFeatures) ви знайдете таблицю перекладу "шаблон / особливість" та кілька приємних дискусій, якщо ви готові копати.


3

Функціональне програмування не замінює схеми дизайну. Шаблони дизайну не можна замінити.

Шаблони просто існують; вони виникли з часом. Книга GoF оформила деякі з них. Якщо нові зразки з’являються на світ, оскільки розробники використовують функціональні мови програмування, що є захоплюючим, і, можливо, про них також будуть написані книги.


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

3
Будь-яка конкретна модель може бути заміною, але концепція шаблонів не може. Пам'ятайте, що термін "візерунок" виник у галузі архітектури .
Френк Ширар

1
Шаблони не призначені для вирішення проблем програмування. Шаблони - це способи, які ми програмуємо. Документація шаблонів призначена для вирішення проблем програмування.
Torbjørn

3
@ Torbjørn: Шаблони - це способи, які ми програмуємо, коли мова перешкоджає . Вони існують через певну невідповідність між бажаною поведінкою програми та вбудованими здібностями мови, де вимоги та здібності не відображаються добре або відображаються неоднозначно. Якби не це було, не було б візерунка; у вас буде одна реалізація, як саме це робиться , і інші реалізації фактично не варто розглядати.
cHao

1
За винятком того, що моделі справді існують лише для полегшення спілкування. Іншої мети немає. І на всіх зустрічах із дизайном, на яких я відвідував ці роки, обговорення алгоритму - це те, що було важливим, а не закономірністю. Шаблон рідко пояснював, що насправді відбувається в будь-якому змістовному сенсі. Чи пояснює це точно вплив O (n) проти O (n Log (n))? Ні. Чи пояснює це, наскільки легко воно вписується в існуючу архітектуру? Ні. Повномасштабне обговорення алгоритму. Я не стверджую, що моделі повинні бути відкликані самі по собі, але якби вони були, майже нічого не постраждало б у результаті.

3

У новій книзі 2013 року під назвою "Шаблони функціонального програмування - у Скалі та Клоджуре" автор Майкл Б. Лінн справляє гідну роботу, порівнюючи та забезпечуючи заміну в багатьох випадках для моделей GoF, а також обговорює нові функціональні зразки, такі як «хвоста рекурсія», «запам'ятовування», «ледача послідовність» тощо.

Ця книга доступна на Amazon. Я вважав це дуже інформативним та обнадійливим, коли виходив з фон OO за кілька десятків років.


3

Уряди ООП та Міністерства фінансів стосуються держав. OOP моделює реальність, щоб підтримувати базу коду якомога ближче до заданих вимог реальності. Шаблони дизайну GoF - це моделі, які були визначені для вирішення атомних проблем у реальному світі. Вони вирішують проблему держави семантично.

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

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

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

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

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


2
ПП може мати стан - просто немає глобального, загального або змінного стану.
vt5491

2

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

Принцип HOF означає, що функції можна передавати як аргументи іншим функціям. і функції можуть повертати значення.


1

Як було сказано у прийнятій відповіді, OOP та FP мають свою специфічну закономірність.

Однак існують такі звичні закономірності, що всі платформи програмування, на які я думаю, повинні мати. Ось (неповний) список:

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

  • Фасад. Будь-які платформи програмування, які можуть обробляти великий вихідний код, повинні мати змогу модулювати. Якщо ви створили модуль для інших частин програми, вам захочеться приховати "брудні" частини коду і надати йому хороший інтерфейс.

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

Також я помітив, що в типовій мові FP, Haskell, є щось подібне до моделей GoF, але з різними назвами. На мою думку, це дозволяє припустити, що вони там були, тому що існують деякі загальні проблеми, які потрібно вирішити як на FP, так і на OOP.

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

1

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

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

Я б не використовував функціональну мову для написання інтерфейсу користувача, але одна з мов OO, наприклад C # або Java, полегшила б цю роботу. Якби я писав функціональну мову, я б не розглядав можливість використання моделей дизайну ОО.


1

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

Однак ці дві парадигми не обов'язково на 100% суперечать і можуть застосовуватися разом, щоб отримати користь з обох світів.

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


1

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

З мого досвіду, розробленого мовою мульти-парадигми (Ruby), реалізація FP добре працює в простих випадках, але там, де контекст є складнішим, підхід на основі GoF OOP краще підходить.

Підхід ПЗ не замінює підхід ООП, а доповнює його.


0

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

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

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


Гм, я мав би помітити, що це питання було одинадцять років, перш ніж відповісти. :-)
Джим Флуд

0

Зберіться з духом.

Багатьох це погіршить, почувши мене, що я замінив дизайнерські зразки та розвінчав ТВЕРДО і СУХО. Я ніхто. Тим не менше, я правильно моделював спільну (виробничу) архітектуру і опублікував правила побудови процесів в Інтернеті разом з кодом та наукою, що стоять за ним на своєму веб-сайті http://www.powersemantics.com/ .

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

  • Виробництво = кожен крок взаємодіє з одним продуктом
  • OOP = кожен крок взаємодіє із собою та іншими модулями, передаючи виріб від точки до точки, як непотрібні офісні працівники

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

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

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


0

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

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