В сообщении в блоге на F # для развлечения и выгоды говорится:
В функциональном дизайне очень важно отделить поведение от данных. Типы данных просты и «тупы». И затем отдельно у вас есть ряд функций, которые действуют на эти типы данных.
Это полная противоположность объектно-ориентированному дизайну, где поведение и данные должны быть объединены. В конце концов, это именно то, что класс. На самом деле в действительно объектно-ориентированном дизайне у вас не должно быть ничего, кроме поведения - данные являются частными и могут быть доступны только через методы.
На самом деле, в OOD отсутствие поведения вокруг типа данных считается плохой вещью и даже имеет название: « модель анемичной области ».
Учитывая, что в C # мы, кажется, продолжаем заимствовать у F # и пытаемся писать более функциональный код; почему мы не заимствуем идею разделения данных / поведения и даже не считаем ее плохой? Это просто то, что определение не подходит для ООП, или есть конкретная причина, по которой это плохо в C #, которая по какой-то причине не применяется в F # (и фактически перевернута)?
(Примечание: меня особенно интересуют различия в C # / F #, которые могут изменить мнение о том, что хорошо / плохо, а не люди, которые могут не согласиться с любым мнением в сообщении в блоге).
источник
Ответы:
Основная причина, по которой FP стремится к этому, а C # OOP - нет, заключается в том, что в FP основное внимание уделяется ссылочной прозрачности; то есть данные входят в функцию и данные выходят, но исходные данные не изменяются.
В C # OOP есть концепция делегирования ответственности, когда вы делегируете управление объектом ему, и поэтому вы хотите, чтобы он изменил свои внутренние компоненты.
В FP вы никогда не хотите изменять значения в объекте, поэтому встроенные в ваш объект функции не имеют смысла.
Кроме того, в FP у вас более высокий родовой полиморфизм, позволяющий вашим функциям быть гораздо более обобщенным, чем позволяет C # OOP. Таким образом, вы можете написать функцию, которая работает для любого
a
, и, следовательно, встроить ее в блок данных не имеет смысла; что бы плотно пара метода так , что он работает только с конкретным видом изa
. Подобное поведение все хорошо и распространено в C # OOP, потому что у вас нет возможности абстрагировать функции вообще, но в FP это компромисс.Самая большая проблема, которую я видел в анемичных моделях доменов в C # OOP, заключается в том, что в итоге вы получаете дублированный код, потому что у вас есть DTO x и 4 разные функции, которые передают действие f в DTO x, потому что 4 разных человека не видели другую реализацию , Когда вы помещаете метод непосредственно в DTO x, тогда все эти 4 человека видят реализацию f и повторно используют ее.
Анемичные модели данных в C # OOP препятствуют повторному использованию кода, но это не относится к FP, потому что одна функция обобщена для стольких различных типов, что вы получаете большее повторное использование кода, поскольку эта функция может использоваться в гораздо большем числе сценариев, чем функция, которую вы написал бы для одного DTO в C #.
Как отмечалось в комментариях , вывод типа является одним из преимуществ, на которые основывается FP, чтобы позволить такой значительный полиморфизм, и, в частности, вы можете проследить это до системы типов Хиндли Милнера с помощью вывода типа Алгоритм W; такой вывод типов в системе типов C # OOP был исключен, потому что время компиляции при добавлении вывода на основе ограничений становится очень длинным из-за необходимого исчерпывающего поиска, подробности здесь: https://stackoverflow.com/questions/3968834/generics-why -cant-The-компилятором Infer-типа-аргументы-в-этом-случае
источник
У вашего вопроса есть большая проблема, которая ограничит полезность получаемых вами ответов: вы подразумеваете / предполагаете, что F # и FP похожи. FP - это огромное семейство языков, включающее переписывание символических терминов, динамическое и статическое. Даже среди статически типизированных языков FP есть много разных технологий для выражения моделей предметной области, таких как модули высшего порядка в OCaml и SML (которых нет в F #). F # является одним из этих функциональных языков, но он особенно примечателен тем, что является обедненным и, в частности, не предоставляет ни модулей более высокого порядка, ни типов более высокого уровня.
На самом деле, я не мог начать рассказывать вам, как доменные модели выражаются в FP. Другой ответ здесь очень конкретно говорит о том, как это делается в Haskell, и совсем не применим к Lisp (прародителю всех языков FP), семейству языков ML или любым другим функциональным языкам.
Обобщения могут рассматриваться как способ разделения данных и поведения. Обобщения происходят от семейства функциональных языков программирования ML, которые не являются частью ООП. У C # есть дженерики, конечно. Таким образом, можно утверждать, что C # постепенно заимствует идею разделения данных и поведения.
Я считаю, что ООП основывается на принципиально иной предпосылке и, следовательно, не дает вам инструментов, необходимых для разделения данных и поведения. Для всех практических целей вам нужен продукт и сумма типов данных и отправка по ним. В ML это означает типы объединения и записи и сопоставление с образцом.
Посмотрите на пример, который я привел здесь .
Будьте осторожны при переходе с ООП на C #. C # далеко не так пуритански по отношению к ООП, как другие языки. .NET Framework теперь полон обобщений, статических методов и даже лямбд.
Отсутствие типов объединения и сопоставления с образцом в C # делает это практически невозможным. Когда у тебя есть только молоток, все выглядит как гвоздь ...
источник
Я думаю, что в бизнес-приложениях вы часто не хотите скрывать данные, потому что сопоставление с образцом для неизменяемых значений отлично подходит для того, чтобы обеспечить охват всех возможных случаев. Но если вы реализуете сложные алгоритмы или структуры данных, вам лучше скрыть детали реализации, превращая ADT (алгебраические типы данных) в ADT (абстрактные типы данных).
источник