Дизайн на «смешанных» языках: объектно-ориентированный дизайн или функциональное программирование?

11

В последние несколько лет языки, которые я люблю использовать, становятся все более и более «функциональными». Сейчас я использую языки, которые являются своего рода «гибридом»: C #, F #, Scala. Мне нравится разрабатывать свое приложение, используя классы, которые соответствуют объектам домена, и использовать функциональные возможности, где это делает кодирование проще, более удобным и безопасным (особенно при работе с коллекциями или при передаче функций).

Однако эти два мира "сталкиваются", когда приходят к разработке шаблонов. Конкретный пример, с которым я недавно столкнулся, - это шаблон Observer. Я хочу, чтобы производитель уведомлял какой-то другой код («потребители / наблюдатели», например, хранилище БД, регистратор и т. Д.), Когда элемент создается или изменяется.

Я изначально сделал это "функционально" так:

producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed

Но теперь я задаюсь вопросом, должен ли я использовать более "ОО" подход:

interface IItemObserver {
  onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...

producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop

Каковы плюсы и минусы двух подходов? Однажды я слышал, как гуру ФП сказал, что шаблоны проектирования существуют только из-за ограничений языка, и поэтому в функциональных языках их так мало. Может быть, это может быть примером этого?

РЕДАКТИРОВАТЬ: В моем конкретном сценарии мне это не нужно, но .. как бы вы реализовали удаление и добавление «наблюдателей» функциональным способом? (Т.е. как бы вы реализовали все функции в шаблоне?) Например, просто передали новую функцию?

Лоренцо Дематте
источник
А как насчет актеров?
Кирицуку
Забудьте про классы и все эти бесполезные вещи. Вместо этого вам лучше подумать о модулях (см. Вдохновение для языков SML и OCaml).
SK-logic
@Antoras Если бы вы могли сравнить с подходом, основанным на актерах, это было бы более чем приветствоваться :)
Lorenzo Dematté
2
@ dema80, OCaml - это идеальная мультипарадигма. Модули вообще не связаны с функциональным программированием. Например, в чисто императивной Аде есть продвинутая модульная система. И вся известность, полученная ООП, должна идти на модули - все хорошее в ООП - это просто различные формы симуляции функциональности модулей. Вы можете полностью забыть все эти классы и использовать их синтаксис для выражения модулей, думая с точки зрения модулей, а не ООП. Кстати, именно это и сделала Microsoft со своим mscorlib - там не так много ООП, только модули и пространства имен.
SK-logic
1
Я думаю, что лучший вопрос: "Есть ли какая-то ясность или организация, которую вы теряете, делая это способом FP?"
Джечлин

Ответы:

0

Это хороший пример двух разных подходов, которые несут идею выполнения задачи за границей вызывающего объекта.

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

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

Филипп Дупанович
источник
Я думаю, что этот ответ немного ошибочен. Функциональное программирование - это не программирование (только с) функций. Функциональное программирование также использует абстракции, поэтому здесь нет дихотомии.
Доваль
«композиция или наследование, где у вас будет немного больше свободы для повторного использования и расширения существующего поведения», вы говорите, что это НЕ одно из основных преимуществ чистого FP. Модульность, компоновка и повторное использование просто происходят естественным образом, когда вы кодируете функционально, это не «вещь ООП»
Сара
@ FilipDupanović Я ссылался на повторное использование и расширение существующего кода. Чистые функции, вероятно, являются самыми сложными в программировании. Вы написаны так, как будто вы не можете управлять сложностью в чисто функциональной среде. Управление сложностью путем объединения простых деталей в более крупные, но все же простые и непрозрачные детали - это все ядро ​​FP, и многие утверждают, что это даже лучше, чем ООП. Я не согласен с дихотомией между «функциональными однострочниками, которые не масштабируют VS Solid OOP, который масштабирует без проблем», которые вы выдвигаете.
Сара
Вы сказали, что FP уступает в составлении и повторном использовании кода и что, когда приложения становятся более сложными, ООП будет более или менее всегда «лучше». Я утверждаю, что это просто ложно и вводит в заблуждение, и поэтому я подумал, что это оправдывает отрицательное мнение. Это действительно "фанатизм пуристов"? Я не буду обсуждать это здесь, так как комментарии не для расширенных обсуждений, подобных этим.
Сара
5

Функциональная версия намного короче, проще в обслуживании, удобнее для чтения и, как правило, значительно превосходит практически все возможные мысли.

Многие, хотя и далеко не все шаблоны , должны компенсировать недостаток функций в ООП, таких как Observers. Это гораздо лучше моделируется функционально.

DeadMG
источник
Я согласен и чувствую то же самое.
Лоренцо Дематте
Я согласен и испытываю те же чувства. Но я как бы «обманывал» мой функциональный код: в моем случае это все, что мне нужно, но что если мне нужно добавить и удалить «наблюдателей»? Я отредактировал свой вопрос
Лоренцо Дематте
5

Ваш "FP-гуру" частично прав; многие ОО-модели - это хаки, которые делают функциональные вещи. (Его утверждение о том, что это причина, по которой мало языков FP, кажется в лучшем случае сомнительным.) Шаблоны Observer и Strategy пытаются эмулировать первоклассные функции. Шаблон посетителя - это хак для симуляции соответствия шаблону. Вы IItemObserverпросто замаскированная функция. Притворение, что оно отличается от любой другой функции, которая берет предмет, ничего не покупает.

Объекты являются лишь одним из видов абстракции данных. Эта статья поможет пролить свет на эту тему. Объекты могут быть полезны, но важно понимать, что они не подходят для всего. Там нет дихотомии; это просто вопрос выбора правильного инструмента для правильной работы, а функциональное программирование ни в коем случае не требует от вас отказа от объектов. Кроме того, функциональное программирование - это больше, чем просто использование функций. Это также о минимизации побочных эффектов и мутаций.

Doval
источник
+1 за (ИМО) хороший ответ. Кроме того, спасибо за ссылку на статью: я прочитал ее некоторое время назад, а затем потерял. Теперь я нашел это снова.
Джорджио
-1

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

Я не согласен с тем, что шаблоны проектирования компенсируют «ограничения» в ОО-языках. Они предназначены для эффективного использования функций ООП. Полиморфизм и наследование являются характеристиками, а не ограничениями. Шаблоны проектирования используют эти функции для продвижения гибкого дизайна. Вы можете сделать абсолютно не-OO программу в OO. Вы можете с большой осторожностью написать целую программу, содержащую объекты, которые не имеют состояния, имитирующие FP.

DPD
источник
1
«Вы можете, с большой осторожностью, написать целую программу, содержащую объекты, которые не имеют состояния, имитирующие FP.», Конечно, и через дисциплину вы можете сделать то же самое на императивных языках. :) В целом, я не думаю, что дизайн-паттерны - это что-то, чтобы компенсировать ограничения, но рассмотрим случай паттерна Visitor.
Лоренцо Дематте,
Кроме того, я не имею отношения к коду: однако я хотел бы столкнуться с другими программистами на подходе (насколько я понимаю, это то, для чего предназначен programmers.se, в противном случае я бы разместил свой Q на SO)
Лоренцо Дематте
Каждый раз, когда вы обнаруживаете повторяющийся шаблон, это не что иное, как указание на серьезное ограничение вашего языка и структуры моделирования. В идеальном мире не должно быть никаких закономерностей.
SK-logic
@ SK-logic: К сожалению, мир не идеален, а в реальном мире есть закономерности. Вот почему у ОО-языков есть наследование, чтобы реализовать повторяемый код без необходимости перезаписывать их заново. У объекта реального мира есть состояние, поэтому существует шаблон «Состояние»
DPD
1
@ SK-logic: Я хорошо знаю, что некоторые языки являются программируемыми, а некоторые допускают применение эффектов и в системе типов. Я хочу сказать, что, как и «ООП», «шаблон» - это, к сожалению, неправильно понятый термин. Шаблон - это то, что постоянно появляется в похожих, но разных формах , которое не допускает единого решения . Иногда вы можете сделать так, чтобы эти квазипрерывания исчезли - мультиметоды делают Visitor / double dispatch избыточным. Это не означает, что «все модели являются признаками недостатка», потому что это означает, что реальный мир несовершенен. Шаблоны возникли в архитектуре , а не в программном обеспечении.
Фрэнк Шеарар,