W poście na blogu na F # dla zabawy i zysku napisano:
W funkcjonalnym projekcie bardzo ważne jest oddzielenie zachowania od danych. Typy danych są proste i „głupie”. A następnie osobno masz wiele funkcji, które działają na te typy danych.
Jest to dokładne przeciwieństwo projektowania obiektowego, w którym zachowanie i dane mają być łączone. W końcu taka właśnie jest klasa. W naprawdę zorientowanym obiektowo projekcie nie powinieneś zachowywać się inaczej - dane są prywatne i można uzyskać do nich dostęp tylko metodami.
W rzeczywistości w przypadku OOD niewystarczające zachowanie wokół typu danych jest uważane za złą rzecz, a nawet ma nazwę: „ anemiczny model domeny ”.
Biorąc pod uwagę, że w C # wydaje się, że nadal pożyczamy od F # i staramy się pisać bardziej funkcjonalny kod; dlaczego nie zapożyczamy pomysłu oddzielenia danych / zachowania, a nawet uważamy to za złe? Czy to po prostu, że definicja nie jest zgodna z OOP, czy jest konkretny powód, że jest zły w C #, który z jakiegoś powodu nie ma zastosowania w F # (i w rzeczywistości jest odwrócony)?
(Uwaga: szczególnie interesują mnie różnice w C # / F #, które mogą zmienić opinię na temat tego, co jest dobre / złe, niż osoby, które mogą nie zgadzać się z którąkolwiek opinią w blogu).
źródło
Odpowiedzi:
Głównym powodem, dla którego FP ma na to cel, a C # OOP nie jest to, że w PR skupiono się na przejrzystości odniesienia; to znaczy, dane przechodzą do funkcji i dane wychodzą, ale oryginalne dane nie są zmieniane.
W C # OOP istnieje koncepcja przekazania odpowiedzialności, w której delegujesz zarządzanie obiektem, a zatem chcesz, aby zmienił on swoje wewnętrzne elementy.
W FP nigdy nie chcesz zmieniać wartości w obiekcie, dlatego posiadanie funkcji osadzonych w obiekcie nie ma sensu.
Dalej w FP masz wyższy rodzaj polimorfizmu, dzięki czemu twoje funkcje są znacznie bardziej uogólnione niż pozwala C # OOP. W ten sposób możesz napisać funkcję, która działa dla dowolnej
a
, a zatem umieszczenie jej w bloku danych nie ma sensu; które szczelnie para sposób tak, że działa tylko z danego rodzaju za
. Takie zachowanie jest powszechne w C # OOP, ponieważ i tak nie masz możliwości abstrakcyjnego generowania funkcji, ale w FP jest to kompromis.Największym problemem, jaki widziałem w modelach anemicznych domen w C # OOP, jest to, że kończysz się duplikatem kodu, ponieważ masz DTO x i 4 różne funkcje, które zatwierdzają działanie f do DTO x, ponieważ 4 różne osoby nie widziały innej implementacji . Kiedy umieścisz metodę bezpośrednio w DTO x, wtedy te 4 osoby zobaczą implementację f i ponownie go użyją.
Anemiczne modele danych w C # OOP utrudniają ponowne użycie kodu, ale nie dzieje się tak w FP, ponieważ pojedyncza funkcja jest uogólniona na tak wiele różnych typów, że uzyskuje się większe ponowne użycie kodu, ponieważ ta funkcja jest użyteczna w o wiele większej liczbie scenariuszy niż funkcja, którą napisałbym dla jednego DTO w C #.
Jak wskazano w komentarzach , wnioskowanie o typach jest jedną z korzyści, na których opiera się FP, umożliwiając tak znaczący polimorfizm, a konkretnie można prześledzić to z powrotem do systemu typów Hindley Milner z wnioskowaniem o typie Algorytm W. takiego wnioskowania typu w systemie typu C # OOP udało się uniknąć, ponieważ czas kompilacji po dodaniu wnioskowania opartego na ograniczeniach staje się wyjątkowo długi ze względu na konieczne wyczerpujące wyszukiwanie, szczegóły tutaj: https://stackoverflow.com/questions/3968834/generics-why -cant-the-compiler-infer-the-type-arguments-in-this-case
źródło
Twoje pytanie ma duży problem, który ograniczy użyteczność otrzymywanych odpowiedzi: sugerujesz / zakładasz, że F # i FP są podobne. FP to ogromna rodzina języków, w tym symboliczne przepisywanie terminów, dynamiczne i statyczne. Nawet wśród statycznie typowanych języków FP istnieje wiele różnych technologii wyrażania modeli domen, takich jak moduły wyższego rzędu w OCaml i SML (które nie istnieją w F #). F # jest jednym z tych języków funkcjonalnych, ale jest szczególnie zauważalny, ponieważ jest szczupły, a w szczególności nie zapewnia modułów wyższego rzędu ani typów bardziej dobranych.
W rzeczywistości nie mogłem zacząć mówić, jak wyrażane są modele domen w FP. Inna odpowiedź tutaj mówi bardzo konkretnie o tym, jak to się robi w Haskell i w ogóle nie ma zastosowania do Lisp (matki wszystkich języków FP), rodziny języków ML ani innych języków funkcjonalnych.
Generyczne można uznać za sposób na rozdzielenie danych i zachowania. Generyczne pochodzą z rodziny funkcjonalnych języków programowania ML nie są częścią OOP. C # ma oczywiście generyczne. Można więc argumentować, że C # powoli zapożycza pomysł oddzielenia danych i zachowania.
Uważam, że OOP opiera się na zupełnie innym założeniu, w związku z czym nie zapewnia narzędzi potrzebnych do oddzielenia danych i zachowania. Do wszystkich praktycznych celów potrzebne są typy produktów i sumy oraz wysyłka nad nimi. W ML oznacza to typy związków i rekordów oraz dopasowanie wzorców.
Sprawdź przykład, który podałem tutaj .
Uważaj na przeskakiwanie z OOP do C #. C # nie jest tak purytaniczny w OOP, jak inne języki. .NET Framework jest teraz pełen ogólnych, statycznych metod, a nawet lambdas.
Brak typów unii i dopasowywania wzorców w języku C # sprawia, że jest to prawie niemożliwe. Gdy masz tylko młotek, wszystko wygląda jak gwóźdź ...
źródło
Myślę, że w aplikacji biznesowej często nie chcesz ukrywać danych, ponieważ dopasowanie wzorca niezmiennych wartości jest świetne, aby zapewnić, że obejmiesz wszystkie możliwe przypadki. Ale jeśli implementujesz złożone algorytmy lub struktury danych, lepiej ukryj szczegóły implementacji przekształcając ADT (algebraiczne typy danych) w ADT (abstrakcyjne typy danych).
źródło