В последние несколько лет языки, которые я люблю использовать, становятся все более и более «функциональными». Сейчас я использую языки, которые являются своего рода «гибридом»: 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
Каковы плюсы и минусы двух подходов? Однажды я слышал, как гуру ФП сказал, что шаблоны проектирования существуют только из-за ограничений языка, и поэтому в функциональных языках их так мало. Может быть, это может быть примером этого?
РЕДАКТИРОВАТЬ: В моем конкретном сценарии мне это не нужно, но .. как бы вы реализовали удаление и добавление «наблюдателей» функциональным способом? (Т.е. как бы вы реализовали все функции в шаблоне?) Например, просто передали новую функцию?
источник
Ответы:
Это хороший пример двух разных подходов, которые несут идею выполнения задачи за границей вызывающего объекта.
Хотя в этом примере ясно, что вам следует придерживаться функционального подхода, в целом это будет зависеть от того, насколько сложным является поведение вызываемого объекта. Если это действительно сложное поведение, когда вы часто применяете подобную логику, а генераторы функций не могут быть использованы для ее четкого выражения, то вы, вероятно, захотите перейти к составу класса или наследованию, где вы будете иметь немного больше свободы для повторного использования и расширения существующего поведения на разовой основе.
Однако я наблюдал одну модель: обычно разработчики изначально используют функциональный подход, и только после того, как возникает потребность в более детальном поведении, они решают перейти на подход, основанный на классах. Я знаю, например, что Django прошел путь от представлений на основе функций, загрузчиков шаблонов и организаторов тестов к классам, как только преимущества и требования стали очевидными, но не раньше.
источник
Функциональная версия намного короче, проще в обслуживании, удобнее для чтения и, как правило, значительно превосходит практически все возможные мысли.
Многие, хотя и далеко не все шаблоны , должны компенсировать недостаток функций в ООП, таких как Observers. Это гораздо лучше моделируется функционально.
источник
Ваш "FP-гуру" частично прав; многие ОО-модели - это хаки, которые делают функциональные вещи. (Его утверждение о том, что это причина, по которой мало языков FP, кажется в лучшем случае сомнительным.) Шаблоны Observer и Strategy пытаются эмулировать первоклассные функции. Шаблон посетителя - это хак для симуляции соответствия шаблону. Вы
IItemObserver
просто замаскированная функция. Притворение, что оно отличается от любой другой функции, которая берет предмет, ничего не покупает.Объекты являются лишь одним из видов абстракции данных. Эта статья поможет пролить свет на эту тему. Объекты могут быть полезны, но важно понимать, что они не подходят для всего. Там нет дихотомии; это просто вопрос выбора правильного инструмента для правильной работы, а функциональное программирование ни в коем случае не требует от вас отказа от объектов. Кроме того, функциональное программирование - это больше, чем просто использование функций. Это также о минимизации побочных эффектов и мутаций.
источник
Я не могу действительно ответить на вопрос, потому что я не очень хорош в функциональных языках; но я думаю, что вы должны меньше заботиться о подходе, пока то, что у вас работает. Из того, что я понимаю, если вы не добавляете больше слушателей в будущем или не меняете слушателей во время выполнения, вы можете очень легко пропустить паттерн Observer здесь.
Я не согласен с тем, что шаблоны проектирования компенсируют «ограничения» в ОО-языках. Они предназначены для эффективного использования функций ООП. Полиморфизм и наследование являются характеристиками, а не ограничениями. Шаблоны проектирования используют эти функции для продвижения гибкого дизайна. Вы можете сделать абсолютно не-OO программу в OO. Вы можете с большой осторожностью написать целую программу, содержащую объекты, которые не имеют состояния, имитирующие FP.
источник