Инженерия программного обеспечения в том виде, в каком она преподается сегодня, полностью сосредоточена на объектно-ориентированном программировании и «естественном» объектно-ориентированном взгляде на мир. Существует подробная методология, которая описывает, как преобразовать модель предметной области в модель класса с помощью нескольких шагов и множества (UML) артефактов, таких как диаграммы вариантов использования или диаграммы классов. Многие программисты усвоили этот подход и имеют хорошее представление о том, как разработать объектно-ориентированное приложение с нуля.
Новый обман - это функциональное программирование, которому учат во многих книгах и учебных пособиях. Но как насчет функциональной разработки программного обеспечения? Читая о Lisp и Clojure, я пришел к двум интересным утверждениям:
Функциональные программы часто разрабатываются снизу вверх, а не сверху вниз («На Лиспе», Пол Грэм)
Функциональные программисты используют Карты, где ОО-программисты используют объекты / классы («Clojure для Java-программистов», доклад Рича Хикли).
Так, какова методология для систематического (основанного на модели?) Дизайна функционального приложения, то есть в Lisp или Clojure? Каковы общие шаги, какие артефакты я использую, как я могу отобразить их из пространства проблемы в пространство решения?
Ответы:
Слава Богу, что разработчики программного обеспечения еще не открыли функциональное программирование. Вот некоторые параллели:
Многие ОО «шаблоны проектирования» фиксируются как функции более высокого порядка. Например, паттерн «Посетитель» известен в функциональном мире как «складка» (или, если вы теоретик с острым умом, «катаморфизм»). В функциональных языках типы данных - это в основном деревья или кортежи, и с каждым типом дерева связан естественный катаморфизм.
Эти функции высшего порядка часто приходят с определенными законами программирования, такими как «свободные теоремы».
Функциональные программисты используют диаграммы гораздо реже, чем ОО-программисты. Многое из того, что выражено в диаграммах ОО, вместо этого выражено в типах или в «сигнатурах», которые вы должны рассматривать как «типы модулей». У Haskell также есть «классы типов», которые немного похожи на тип интерфейса.
Те функциональные программисты, которые используют типы, обычно думают, что «как только вы получите правильные типы, код практически сам себя напишет».
Не все функциональные языки используют явные типы, но книга How To Design Programs , отличная книга для изучения Scheme / Lisp / Clojure, в значительной степени опирается на «описания данных», которые тесно связаны с типами.
Любой метод проектирования, основанный на абстракции данных, работает хорошо. Мне кажется, что это проще, когда язык имеет явные типы, но работает даже без него. Хорошей книгой о методах проектирования для абстрактных типов данных, которая легко адаптируется к функциональному программированию, является « Абстракция и спецификация в разработке программ » Барбары Лисков и Джона Гуттага, первое издание. Лисков получил премию Тьюринга частично за эту работу.
Другая методология проектирования, уникальная для Lisp, заключается в том, чтобы решить, какие языковые расширения будут полезны в проблемной области, в которой вы работаете, и затем использовать гигиенические макросы для добавления этих конструкций в ваш язык. Хорошее место для чтения об этом типе дизайна - статья Мэтью Флэтта « Создание языков в ракетке» . Статья может быть за платным доступом. Вы также можете найти более общий материал об этом типе дизайна, выполнив поиск по термину «встроенный язык для конкретного домена»; За конкретными советами и примерами, выходящими за рамки того, что рассматривает Мэтью Флэтт, я бы, вероятно, начал с Грэма На Лиспе или, возможно, ANSI Common Lisp .
Общие шаги:
Определите данные в вашей программе и операциях над ней и определите абстрактный тип данных, представляющий эти данные.
Определите общие действия или шаблоны вычислений и выразите их как функции или макросы более высокого порядка. Ожидайте сделать этот шаг как часть рефакторинга.
Если вы используете типизированный функциональный язык, используйте средство проверки типов рано и часто. Если вы используете Lisp или Clojure, лучше всего сначала написать функциональные контракты, включая модульные тесты - это разработка на основе тестов по максимуму. И вы захотите использовать любую версию QuickCheck, перенесенную на вашу платформу, которая в вашем случае выглядит так, как будто она называется ClojureCheck . Это чрезвычайно мощная библиотека для построения случайных тестов кода, использующая функции высшего порядка.
источник
Для Clojure я рекомендую вернуться к старому доброму реляционному моделированию. Out of the Tarpit - это вдохновляющее чтение.
источник
Лично я считаю, что все обычные хорошие практики разработки ОО применимы и к функциональному программированию - всего лишь с несколькими незначительными изменениями, чтобы учесть функциональное мировоззрение. С точки зрения методологии, вам не нужно делать что-то принципиально другое.
Мой опыт основан на том, что в последние годы я перешел с Java на Clojure.
Некоторые примеры:
Понимать вашу бизнес-область / модель данных - одинаково важно, собираетесь ли вы проектировать объектную модель или создать функциональную структуру данных с вложенными картами. В некотором смысле, FP может быть проще, потому что побуждает вас думать о модели данных отдельно от функций / процессов, но вам все равно придется делать и то, и другое.
Сервисная ориентация в дизайне - на самом деле очень хорошо работает с точки зрения FP, поскольку типичный сервис - это просто функция с некоторыми побочными эффектами. Я думаю, что взгляд «снизу вверх» на разработку программного обеспечения, иногда поддерживаемый в мире Lisp, на самом деле является просто хорошими сервис-ориентированными принципами проектирования API в другом облике.
Разработка через тестирование - хорошо работает на языках FP, на самом деле иногда даже лучше, потому что чистые функции очень хорошо подходят для написания четких, повторяемых тестов без необходимости настройки среды с состоянием. Возможно, вы также захотите создать отдельные тесты для проверки целостности данных (например, содержит ли эта карта все ключи, которые я ожидаю, чтобы уравновесить тот факт, что в языке OO определение класса будет применять это для вас во время компиляции).
Прототипирование / итерация - работает так же хорошо с FP. Вы даже можете создавать прототипы с пользователями, если вы очень хорошо умеете создавать инструменты / DSL и использовать их в REPL.
источник
ОО программирование тесно связывает данные с поведением. Функциональное программирование разделяет их. Таким образом, у вас нет диаграмм классов, но у вас есть структуры данных, и у вас, в частности, есть алгебраические типы данных. Эти типы могут быть написаны так, чтобы очень точно соответствовать вашему домену, включая исключение невозможных значений по построению.
Таким образом, на нем нет книг и книг, но существует устоявшийся подход к тому, чтобы, как говорится, сделать невозможные ценности непредставимыми.
При этом вы можете сделать ряд вариантов представления определенных типов данных вместо функций и, наоборот, вместо этого представить некоторые функции как объединение типов данных, чтобы вы могли получить, например, сериализацию, более точную спецификацию, оптимизацию и т. Д. ,
Затем, учитывая это, вы пишете функции над своими объявлениями, так что вы устанавливаете какую-то алгебру - то есть существуют фиксированные законы, которые выполняются для этих функций. Некоторые могут быть идемпотентными - то же самое после нескольких применений. Некоторые ассоциативны. Некоторые переходные и т. Д.
Теперь у вас есть домен, в котором у вас есть функции, которые сочетаются в соответствии с хорошо себя ведущими законами. Простой встроенный DSL!
Да, и с учетом свойств, вы, конечно, можете написать автоматические рандомизированные тесты их (аля QuickCheck) .. и это только начало.
источник
Объектно-ориентированный дизайн - это не то же самое, что разработка программного обеспечения. Разработка программного обеспечения связана со всем процессом перехода от требований к работающей системе, вовремя и с низким уровнем дефектов. Функциональное программирование может отличаться от ОО, но оно не отменяет требований, высокого уровня и детального проектирования, верификации и тестирования, метрик программного обеспечения, оценки и всего прочего «разработки программного обеспечения».
Кроме того, функциональные программы демонстрируют модульность и другую структуру. Ваши подробные проекты должны быть выражены в терминах концепций в этой структуре.
источник
Один из подходов заключается в создании внутреннего DSL на выбранном языке функционального программирования. «Модель» - это набор бизнес-правил, выраженных в DSL.
источник
Смотрите мой ответ на другой пост:
Как Clojure подходит к разделению интересов?
Я согласен с тем, что необходимо написать больше о том, как структурировать большие приложения, использующие подход FP (плюс еще нужно сделать, чтобы документировать пользовательские интерфейсы, управляемые FP)
источник
Хотя это может считаться наивным и упрощенным, я думаю, что «рецепты проектирования» (системный подход к решению проблем, применяемый к программированию, как его поддержал Феллайзен и др. В их книге HtDP ) будут близки к тому, что вы, похоже, ищете.
Здесь несколько ссылок:
http://www.northeastern.edu/magazine/0301/programming.html
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371
источник
Я недавно нашел эту книгу: Функциональное и реактивное моделирование доменов.
Я думаю, что полностью соответствует вашему вопросу.
Из описания книги:
источник
Существует стиль «расчет программы» / «дизайн по расчету», связанный с профессором Ричардом Бердом и группой «Алгебра программирования» в Оксфордском университете (Великобритания), я не думаю, что это слишком надуманно, чтобы считать это методологией.
Лично мне нравится работа, проделанная группой AoP, но у меня нет дисциплины, чтобы практиковать дизайн таким образом. Однако это мой недостаток, а не расчет программы.
источник
Я обнаружил, что Behavior Driven Development подходит для быстро развивающегося кода в Clojure и SBCL. Реальная польза от использования BDD с функциональным языком заключается в том, что я склонен писать гораздо более тонкие модульные тесты, чем обычно, когда использую процедурные языки, потому что я гораздо лучше разбираю проблему на более мелкие фрагменты функциональности.
источник
Честно говоря, если вам нужны рецепты дизайна для функциональных программ, взгляните на стандартные библиотеки функций, такие как Prelude на Haskell. В FP шаблоны обычно фиксируются самими процедурами более высокого порядка (функциями, которые работают над функциями). Таким образом, если шаблон виден, часто функция более высокого порядка просто создается для захвата этого шаблона.
Хороший пример - fmap. Эта функция принимает функцию в качестве аргумента и применяет ее ко всем «элементам» второго аргумента. Поскольку он является частью класса типов Functor, любой экземпляр Functor (например, список, граф и т. Д.) Может быть передан в качестве второго аргумента этой функции. Он отражает общее поведение применения функции к каждому элементу второго аргумента.
источник
Хорошо,
Как правило, многие функциональные языки программирования используются в университетах в течение длительного времени для решения «небольших проблем с игрушками».
Сейчас они становятся все более популярными, поскольку ООП испытывает трудности с «параллельным программированием» из-за «состояния». И иногда функциональный стиль лучше подходит для таких задач, как Google MapReduce.
Я уверен, что когда ребята из functioanl столкнутся со стеной [попытаются внедрить системы, содержащие более 1 000 000 строк кода], некоторые из них получат новые методологии разработки программного обеспечения с модными словечками :-). Они должны ответить на старый вопрос: как разделить систему на куски, чтобы мы могли «кусать» каждый кусочек по одному? [работать итеративно, инцераментально и эволюционно] с использованием функционального стиля.
Но будут ли функциональные программы использоваться для таких больших систем, станут ли они основным потоком? Это вопрос .
И никто не может прийти с реалистичной методологией, не внедрив такие большие системы, не испачкав руки. Сначала вы должны испачкать руки, а затем предложить решение. Решения-предложения без «настоящих болей и грязи» будут «фантазией».
источник