Cytowany post na blogu jest nieco zawyżony. FP nie eliminuje potrzeby tworzenia wzorów. Termin „wzorce projektowe” po prostu nie jest powszechnie używany do opisania tego samego w językach FP. Ale istnieją. Języki funkcjonalne mają wiele zasad najlepszych praktyk w formie „gdy napotkasz problem X, użyj kodu, który wygląda jak Y”, co jest w zasadzie wzorcem projektowym.
Prawdą jest jednak, że większość wzorców projektowych specyficznych dla OOP jest praktycznie nieistotna w językach funkcjonalnych.
Nie sądzę, że powinno to być szczególnie kontrowersyjna powiedzieć, że wzorce projektowe w ogóle istnieć tylko załatać braki w języku. A jeśli inny język może rozwiązać ten sam problem w sposób trywialny, ten inny język nie będzie potrzebował wzorca projektowego. Użytkownicy tego języka mogą nawet nie zdawać sobie sprawy, że problem istnieje , ponieważ cóż, w tym języku nie jest to problem.
Oto, co Gang of Four ma do powiedzenia na ten temat:
Wybór języka programowania jest ważny, ponieważ wpływa na własny punkt widzenia. Nasze wzorce zakładają funkcje języka na poziomie Smalltalk / C ++, a wybór ten określa, co można, a czego nie można łatwo wdrożyć. Gdybyśmy przyjęli języki proceduralne, moglibyśmy uwzględnić wzorce projektowe zwane „Dziedziczeniem”, „Enkapsulacją” i „Polimorfizmem”. Podobnie, niektóre z naszych wzorców są obsługiwane bezpośrednio przez mniej popularne języki obiektowe. CLOS ma na przykład wiele metod, które zmniejszają potrzebę wzorca, takiego jak Visitor. W rzeczywistości istnieje wystarczająca różnica między Smalltalk i C ++, aby oznaczać, że niektóre wzorce można wyrazić łatwiej w jednym języku niż w drugim. (Zobacz na przykład Iterator.)
(Powyższy cytat z książki Wprowadzenie do wzorców projektowych, strona 4, akapit 3)
Główne cechy programowania funkcjonalnego obejmują funkcje jako wartości pierwszej klasy, curry, niezmienne wartości itp. Nie wydaje mi się oczywiste, że wzorce projektowe OO zbliżają którąkolwiek z tych cech.
Jaki jest wzorzec poleceń, jeśli nie przybliżenie funkcji pierwszej klasy? :) W języku FP wystarczy przekazać funkcję jako argument do innej funkcji. W języku OOP musisz zawinąć funkcję w klasę, którą możesz utworzyć, a następnie przekazać ten obiekt do innej funkcji. Efekt jest taki sam, ale w OOP nazywa się to wzorcem projektowym i zajmuje o wiele więcej kodu. A jaki jest abstrakcyjny wzór fabryki, jeśli nie curry? Przekaż parametry do funkcji po trochu, aby skonfigurować, jaka wartość będzie wyrzucana, gdy w końcu ją wywołasz.
Tak więc, kilka wzorców projektowych GoF jest nadmiarowych w językach FP, ponieważ istnieją bardziej wydajne i łatwiejsze w użyciu alternatywy.
Ale oczywiście nadal istnieją wzorce projektowe, których nie rozwiązują języki FP. Jaka jest równowartość FP singletona? (Pomijając przez chwilę to, że singletony są na ogół okropnym wzorem do użycia.)
I działa to w obie strony. Jak powiedziałem, FP ma również swoje wzorce projektowe; ludzie zwykle nie myślą o nich jako o takich.
Być może natknąłeś się na monady. Co to jest, jeśli nie wzór do „radzenia sobie ze stanem globalnym”? Jest to problem tak prosty w językach OOP, że nie istnieje tam równoważny wzorzec projektowy.
Nie potrzebujemy wzorca projektowego do „zwiększania zmiennej statycznej” lub „odczytu z tego gniazda”, ponieważ jest to tylko to, co robisz .
Mówienie, że monada jest wzorcem projektowym, jest równie absurdalne, jak twierdzenie, że liczby całkowite z ich zwykłymi operacjami, a element zerowy jest wzorcem projektowym. Nie, monada to wzór matematyczny , a nie wzór projektowy.
W (czystych) językach funkcjonalnych efekty uboczne i stan mutable są niemożliwe, chyba że obejdziesz je z monadowym „wzorcem projektowym” lub innymi metodami pozwalającymi na to samo.
Ponadto w językach funkcjonalnych, które obsługują OOP (takich jak F # i OCaml), wydaje mi się oczywiste, że programiści używający tych języków używają tych samych wzorców projektowych, które są dostępne dla każdego innego języka OOP. W rzeczywistości teraz używam F # i OCaml codziennie i nie ma uderzających różnic między wzorami używanymi w tych językach a wzorami używanymi podczas pisania w Javie.
Może dlatego, że nadal myślisz imperatywnie? Wiele osób, które przez całe życie miały do czynienia z imperatywnymi językami, z trudem rezygnuje z tego nawyku, próbując języka funkcjonalnego. (Widziałem kilka dość zabawnych prób na F #, gdzie dosłownie każda funkcja była tylko ciągiem instrukcji „let”, zasadniczo tak, jakbyś wziął program C i zastąpił wszystkie średniki słowem „let”. :))
Ale inną możliwością może być to, że po prostu nie zdajesz sobie sprawy, że rozwiązujesz problemy w sposób trywialny, które wymagałyby wzorców projektowych w języku OOP.
Kiedy używasz curry lub przekazujesz funkcję jako argument innemu, zatrzymaj się i pomyśl o tym, jak zrobiłbyś to w języku OOP.
Czy istnieje jakaś prawda w twierdzeniu, że programowanie funkcjonalne eliminuje potrzebę wzorców projektowych OOP?
Tak. :) Kiedy pracujesz w języku FP, nie potrzebujesz już wzorów projektowania specyficznych dla OOP. Nadal jednak potrzebujesz ogólnych wzorców projektowych, takich jak MVC lub inne rzeczy niezwiązane z OOP, a zamiast tego potrzebujesz kilku nowych „wzorców projektowych” specyficznych dla FP. Wszystkie języki mają swoje wady, a wzorce projektowe są zwykle takie, jak je omawiamy.
W każdym razie może być interesujące wypróbowanie swoich „czystszych” języków FP, takich jak ML (mój osobisty ulubiony, przynajmniej do celów edukacyjnych) lub Haskell , gdzie nie masz kuli OOP, na której można polegać , gdy napotyka coś nowego.
Zgodnie z oczekiwaniami kilka osób sprzeciwiło się mojej definicji wzorców projektowych jako „łatanie niedociągnięć w języku”, więc oto moje uzasadnienie:
Jak już powiedziano, większość wzorców projektowych jest charakterystyczna dla jednego paradygmatu programowania, a czasem nawet jednego określonego języka. Często rozwiązują problemy, które istnieją tylko w tym paradygmacie (patrz monady dla FP lub abstrakcyjne fabryki dla OOP).
Dlaczego abstrakcyjny wzór fabryczny nie istnieje w FP? Ponieważ problem, który próbuje rozwiązać, nie istnieje.
Tak więc, jeśli istnieje problem w językach OOP, który nie występuje w językach FP, to oczywiście jest to wada języków OOP. Problem można rozwiązać, ale twój język tego nie robi, ale wymaga od ciebie garstki kodu do obejścia. Idealnie byłoby, gdyby nasz język programowania w magiczny sposób rozwiązał wszystkie problemy. Jakikolwiek problem, który nadal występuje, jest w zasadzie wadą języka. ;)