Это часть серии: « Создание реальных приложений на F # »

Подумать о том, как и где собрать части для приложения Dumia, - это, пожалуй, самая сложная часть создания этого приложения. Чтобы придумать удобный повторяемый способ собрать воедино приложение с использованием функционального языка, такого как F #, требуется реальное изменение мышления.

Сдвиг парадигмы - инкапсуляция против изоляции

Прежде чем мы углубимся в детали сборки приложения с помощью F # или любого другого истинно функционального языка в этом отношении, нам необходимо понять фундаментальное различие в философии между объектно-ориентированным миром и функциональным миром. Это можно свести к двум концепциям: инкапсуляция и изоляция.

Инкапсуляция

Вы видите, что в OOP Land у вас был набор инструментов (классов), которые имели определенное применение.

У каждого инструмента есть внутренняя работа и поведение, которые вы, пользователь, не должны видеть. Вроде как черный ящик. Пользователь должен знать

  • Как создать его экземпляр
  • Какие методы вызывать
  • В каком порядке делать выше

Затем мы собирали приложение, соединяя объекты вместе, позволяя объектам создавать другие объекты или вызывая другие объекты. Из-за этого поведение пользователя зависело от ожиданий Объектов, от которых он зависел. В результате на практике их было трудно изолировать, что по определению означало, что их было трудно тестировать, потому что вам нужно было изолировать код для его тестирования.

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

Изоляция

В функциональном программировании вы имеете дело с функциями как с основным строительным блоком. Каждую функцию можно представить себе как кирпичик лего

Чистая функция по определению изолирована. В отличие от инкапсуляции, когда объект пытается что-то скрыть от вас, чистая функция не может делать ничего, что не было объявлено в ее интерфейсе (или подписи). Можно сказать, что функция Pure «честная»

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

Сначала это может показаться нелогичным, но затем, когда вы откроете для себя возможности, это в корне изменит ваше представление о создании программного обеспечения.

Из-за моего обширного опыта работы с объектно-ориентированными языками этот сдвиг парадигмы произвел так много когнитивного диссонанса о том, как структурировать приложение Dumia, что я буквально провел бессонную ночь, пытаясь избавиться от своих старых привычек. с новой парадигмой

В некоторых моментах дизайна мне пришлось отступить и подумать:

При таком подходе к архитектуре приложения отпадает много сложностей. Вам больше не нужны интерфейсы, макеты, менеджеры, службы, докладчики, контроллеры, и этот список можно продолжить. На самом деле, это изображение Скотта Влашина попадает в самую точку.

Имея это в виду, я придумал очень простую функциональную архитектуру.

Я смог разделить функции на два лагеря и как таковые объединить их в две сборки.

  • Домен - содержит функции, которые представляют собой «чистую» бизнес-логику (или правила), что означает, что они не зависят от среды приложения (веб, консоль, мобильное устройство и т. д.). Эти функции, как правило, являются чистыми и имеют дело только с преобразованием входных данных и созданием выходных данных. Вы должны иметь возможность взять этот код и легко перенести его из одной среды в другую.
  • Инфраструктура - эта группа содержит функции, которые должны либо взаимодействовать с внешним миром (например, функции DataAccess или функции SMS / электронной почты), либо работать с внешними зависимостями. (например, библиотека криптографии)

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

Примером этого является использование функций высшего порядка (функций, которые принимают другие функции в качестве параметров) как формы полиморфизма, а не интерфейсов.

Например, вместо интерфейса IEncryption с функцией Encrypt, которая принимает строку и возвращает строку, он будет заменен функцией, которая принимает строку и возвращает нить. Эта функция передается в любую функцию, которая должна шифровать строки.

См. .. Контейнеры / интерфейсы IOC не требуются.

Сделав это, я получил что-то вроде этого