Чи функції першого класу є замінником структури стратегії?


15

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

Так, наприклад, скажіть, що ви хотіли передати функціональність об'єкту. У Java вам доведеться передати об’єкту ще один об'єкт, який інкапсулює бажану поведінку. Мовою, такою як Ruby, ви просто передасте функціональність у вигляді анонімної функції.

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

Це тому, що об’єкт може утримувати стан, який існує незалежно від періоду, коли він працює методом. Однак анонімна функція сама по собі може утримувати стан, який перестає існувати в момент закінчення виконання функції.

Чи має в об'єктно-орієнтованій мові, яка підтримує функції першого класу, якась стратегія має якусь перевагу перед використанням функцій?


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

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

Відповіді:


13

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

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

  • Route findShortestRoute(Node start, Node destination)
  • boolean doesRouteExist(Node start, Node destination)
  • Route[] findAllPossibleRoutes(Node start, Node destination)
  • Route findShortestRouteToClosestDestination(Node start, Node[] destinations)
  • Route findTravelingSalesmanRoute(Node[] stations)

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

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


2
"коли цей внутрішній стан стає дуже складним, він часто стає корисним для сприяння закриттю до повноцінного класу": Чому? Якщо внутрішній стан стає складним, ви також можете помістити його в об'єкт / запис, який зберігається всередині закриття.
Джорджіо

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

Мені це здається розумним поглядом.
Джорджіо

5

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

Візьміть такий приклад у Common Lisp:

(defun number-strings (ss)
  (let ((counter 0))
    (mapcar #'(lambda (s) (format nil "~a: ~a" (incf counter) s)) ss)))

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

(number-strings '("a" "b" "c"))

дає

("1: a" "2: b" "3: c")

Функція number-stringsвнутрішньо використовує анонімну функцію зі змінною, counterяка містить стан (поточне значення лічильника), яка повторно використовується щоразу, коли функція викликається.

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

Зокрема, для стратегії потрібен об'єкт лише одним методом, тому закриття повинно виконувати роботу. Але, як зауважив Філіп у своїй відповіді, залежно від обставин (складного стану) та мов програмування ви можете отримати більш елегантне рішення, використовуючи об’єкти.


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

1
Я схильний погоджуватися з Філіппом: це залежить від мови та особистих уподобань. Я завжди вибирав би той підхід, який робить позначення максимально простим. Наприклад, в Lisp я міг визначити свій стан як список змінних через a, letа потім визначити моє закриття всередині нього. В основному, я б визначив об'єкт одним методом на льоту. Іншою мовою (наприклад, Java) може бути зручніше (синтаксично простіше) визначити належний об'єкт для утримання стану. Отже, я вирішив би від випадку до випадку.
Джорджіо

1

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

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

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


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

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

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

1

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

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


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

Я схильний погодитися з @Snowman. Ви впевнені, що говорите про шаблон стратегії ?
Роуан Фрімен

1
@Snowman, навіть сторінка, на яку ви пов’язані, не вказує, як саме ця модель має бути реалізована, вони наводять приклади певними мовами, але в діаграмі UML немає нічого, що говорить про те, що мені потрібно використовувати успадкування C ++, інтерфейси Java або блоки Ruby . Тому я люб'язно не згоден з вашим аналізом.
idoby
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.