La publicación de blog que citó exagera un poco su reclamo. FP no elimina la necesidad de patrones de diseño. El término "patrones de diseño" simplemente no se usa ampliamente para describir lo mismo en lenguajes FP. Pero ellos existen. Los lenguajes funcionales tienen muchas reglas de mejores prácticas de la forma "cuando encuentre el problema X, use código que se parezca a Y", que es básicamente lo que es un patrón de diseño.
Sin embargo, es correcto que la mayoría de los patrones de diseño específicos de OOP son prácticamente irrelevantes en lenguajes funcionales.
No creo que deba ser particularmente controvertido decir que los patrones de diseño en general solo existen para corregir las deficiencias en el lenguaje. Y si otro idioma puede resolver el mismo problema de manera trivial, ese otro idioma no necesitará un patrón de diseño para ello. Es posible que los usuarios de ese idioma ni siquiera sepan que el problema existe , porque, bueno, no es un problema en ese idioma.
Esto es lo que la Banda de los Cuatro tiene que decir sobre este tema:
La elección del lenguaje de programación es importante porque influye en el punto de vista. Nuestros patrones asumen características de lenguaje de nivel Smalltalk / C ++, y esa elección determina lo que puede y no puede implementarse fácilmente. Si asumimos lenguajes de procedimiento, podríamos haber incluido patrones de diseño llamados "Herencia", "Encapsulación" y "Polimorfismo". Del mismo modo, algunos de nuestros patrones son compatibles directamente con los lenguajes orientados a objetos menos comunes. CLOS tiene múltiples métodos, por ejemplo, que disminuyen la necesidad de un patrón como Visitor. De hecho, existen suficientes diferencias entre Smalltalk y C ++ para que algunos patrones se puedan expresar más fácilmente en un idioma que en el otro. (Ver Iterator, por ejemplo).
(Lo anterior es una cita del libro Introducción a los patrones de diseño, página 4, párrafo 3)
Las características principales de la programación funcional incluyen funciones como valores de primera clase, curry, valores inmutables, etc. No me parece obvio que los patrones de diseño OO se aproximen a cualquiera de esas características.
¿Cuál es el patrón de comando, si no una aproximación de funciones de primera clase? :) En un lenguaje FP, simplemente pasaría una función como argumento a otra función. En un lenguaje OOP, debe concluir la función en una clase, que puede instanciar y luego pasar ese objeto a la otra función. El efecto es el mismo, pero en OOP se llama patrón de diseño y requiere mucho más código. ¿Y cuál es el patrón abstracto de fábrica, si no es curry? Pase los parámetros a una función poco a poco, para configurar qué tipo de valor escupe cuando finalmente lo llama.
Entonces, sí, varios patrones de diseño de GoF se vuelven redundantes en lenguajes FP, porque existen alternativas más potentes y fáciles de usar.
Pero, por supuesto, todavía hay patrones de diseño que los lenguajes FP no resuelven. ¿Cuál es el equivalente de FP de un singleton? (Sin tener en cuenta por un momento que los singletons son generalmente un patrón terrible de usar).
Y funciona en ambos sentidos también. Como dije, FP también tiene sus patrones de diseño; la gente no suele pensar en ellos como tal.
Pero es posible que haya encontrado mónadas. ¿Cuáles son, si no un patrón de diseño para "tratar con el estado global"? Ese es un problema tan simple en los lenguajes OOP que no existe un patrón de diseño equivalente allí.
No necesitamos un patrón de diseño para "incrementar una variable estática" o "leer desde ese socket", porque es justo lo que haces .
Decir que una mónada es un patrón de diseño es tan absurdo como decir los números enteros con sus operaciones habituales y el elemento cero es un patrón de diseño. No, una mónada es un patrón matemático , no un patrón de diseño.
En lenguajes funcionales (puros), los efectos secundarios y el estado mutable son imposibles, a menos que trabaje con el "patrón de diseño" de la mónada o con cualquiera de los otros métodos para permitir lo mismo.
Además, en lenguajes funcionales que admiten OOP (como F # y OCaml), me parece obvio que los programadores que usan estos lenguajes usarían los mismos patrones de diseño que se encuentran disponibles para cualquier otro lenguaje OOP. De hecho, en este momento uso F # y OCaml todos los días, y no hay diferencias notables entre los patrones que uso en estos lenguajes y los patrones que uso cuando escribo en Java.
¿Quizás porque todavía estás pensando imperativamente? Muchas personas, después de lidiar con idiomas imperativos toda su vida, tienen dificultades para abandonar ese hábito cuando prueban un lenguaje funcional. (He visto algunos intentos bastante divertidos en F #, donde literalmente cada función era solo una cadena de declaraciones 'let', básicamente como si hubiera tomado un programa en C y reemplazado todos los puntos y coma con 'let'. :))
Pero otra posibilidad podría ser que no se haya dado cuenta de que está resolviendo problemas trivialmente que requerirían patrones de diseño en un lenguaje OOP.
Cuando use curry o pase una función como argumento a otra, deténgase y piense cómo lo haría en un lenguaje OOP.
¿Hay algo de cierto en la afirmación de que la programación funcional elimina la necesidad de patrones de diseño OOP?
Sí. :) Cuando trabajas en un lenguaje FP, ya no necesitas los patrones de diseño específicos de OOP. Pero aún necesita algunos patrones de diseño generales, como MVC u otras cosas no específicas de OOP, y necesita un par de nuevos "patrones de diseño" específicos de FP en su lugar. Todos los idiomas tienen sus defectos, y los patrones de diseño suelen ser la forma en que trabajamos en torno a ellos.
De todos modos, puede que le resulte interesante probar los lenguajes FP "más limpios", como ML (mi favorito personal, al menos con fines de aprendizaje) o Haskell , donde no tiene la muleta OOP para recurrir cuando Te enfrentas a algo nuevo.
Como era de esperar, algunas personas se opusieron a mi definición de patrones de diseño como "parchear las deficiencias en un idioma", así que aquí está mi justificación:
Como ya se dijo, la mayoría de los patrones de diseño son específicos de un paradigma de programación o, a veces, incluso de un lenguaje específico. A menudo, resuelven problemas que solo existen en ese paradigma (ver mónadas para FP, o fábricas abstractas para OOP).
¿Por qué no existe el patrón abstracto de fábrica en FP? Porque el problema que intenta resolver no existe allí.
Entonces, si existe un problema en los idiomas OOP, que no existe en los idiomas FP, entonces claramente es una deficiencia de los idiomas OOP. El problema se puede resolver, pero su idioma no lo hace, sino que requiere un montón de código repetitivo de su parte para solucionarlo. Idealmente, nos gustaría que nuestro lenguaje de programación mágicamente solucione todos los problemas. Cualquier problema que todavía exista es, en principio, una deficiencia del lenguaje. ;)