¿La programación funcional reemplaza los patrones de diseño de GoF?

1047

Desde que comencé a aprender F # y OCaml el año pasado, he leído una gran cantidad de artículos que insisten en que los patrones de diseño (especialmente en Java) son soluciones para las características que faltan en los lenguajes imperativos. Un artículo que encontré hace una afirmación bastante fuerte :

La mayoría de las personas que he conocido han leído el libro Design Patterns de The Gang of Four (GoF). Cualquier programador que se respete a sí mismo le dirá que el libro es independiente del lenguaje y que los patrones se aplican a la ingeniería de software en general, independientemente del idioma que utilice. Este es un reclamo noble. Lamentablemente, está muy lejos de la verdad.

Los lenguajes funcionales son extremadamente expresivos. En un lenguaje funcional, uno no necesita patrones de diseño porque es probable que el lenguaje tenga un nivel tan alto que termine programando en conceptos que eliminen todos los patrones de diseño.

Las características principales de la programación funcional (FP) 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.

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.

¿Hay algo de cierto en la afirmación de que la programación funcional elimina la necesidad de patrones de diseño OOP? Si es así, ¿podría publicar o vincular a un ejemplo de un patrón de diseño OOP típico y su equivalente funcional?

Julieta
fuente
18
Puede mirar el artículo de Steve Yegge ( steve-yegge.blogspot.com/2006/03/… )
Ralph
27
"El libro es independiente del lenguaje y los patrones se aplican a la ingeniería de software en general". Cabe señalar que el libro no está de acuerdo con esta afirmación, en el sentido de que algunos idiomas no necesitan expresar ciertas cosas como los patrones de diseño: "Nuestros patrones asume las características de lenguaje de nivel Smalltalk / C ++, y esa elección determina qué puede y qué no puede implementarse fácilmente [...] CLOS tiene métodos múltiples, por ejemplo, que disminuyen la necesidad de un patrón como el Visitante (Página 331) ". (página 4)
Guildenstern
66
También tenga en cuenta que muchos patrones de diseño ni siquiera son necesarios en lenguajes imperativos de nivel suficientemente alto.
R. Barzell
3
@ R.Barzell ¿Cuáles son esos "lenguajes imperativos de nivel suficientemente alto"? Gracias.
cibercitizen1
55
@ cibercitizen1 idiomas de tipo pato con soporte para funciones de orden superior y funciones anónimas. Estas características proporcionan gran parte de la potencia que muchos patrones de diseño debían proporcionar.
R. Barzell

Respuestas:

1077

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. ;)

jalf
fuente
73
Los patrones de diseño describen soluciones generales a problemas básicos. Pero eso también es lo que hacen los lenguajes de programación y las plataformas. Por lo tanto, utiliza patrones de diseño cuando los idiomas y las plataformas que está utilizando no son suficientes.
yfeldblum
135
S.Lott: Describen soluciones a problemas que existen en un idioma dado, sí. No existe un patrón de diseño de Comando en lenguajes FP, porque el problema que intenta resolver no existe. Lo que significa que resuelven problemas que el lenguaje en sí no puede resolver. Es decir, deficiencias en el idioma
jalf
38
La mónada es un concepto matemático, y lo está estirando con su clasificación. Claro, puede ver funciones, monoides, mónadas, matrices u otros conceptos matemáticos como patrones de diseño, pero esos son más como algoritmos y estructuras de datos ... conceptos fundamentales, independientes del lenguaje.
Alexandru Nedelcu el
41
Sin duda, las mónadas son un concepto matemático, sino que son también un patrón. El "patrón FP" de las mónadas es algo distinto del concepto matemático de las mónadas. El primero es un patrón utilizado para sortear ciertas "limitaciones" en lenguajes FP puros. Este último es un concepto matemático universal.
jalf
69
Tenga en cuenta que las mónadas en Haskell se utilizan para otras cosas que no sean el estado mutable, por ejemplo, para excepciones, continuaciones, listas de comprensión, análisis, programación asincrónica, etc. Pero todas estas aplicaciones de mónadas probablemente podrían llamarse patrones.
JacquesB
152

¿Hay algo de cierto en la afirmación de que la programación funcional elimina la necesidad de patrones de diseño OOP?

La programación funcional no es lo mismo que la programación orientada a objetos. Los patrones de diseño orientados a objetos no se aplican a la programación funcional. En cambio, tiene patrones de diseño de programación funcional.

Para la programación funcional, no leerá los libros de patrones de diseño OO; leerás otros libros sobre patrones de diseño FP.

agnóstico del lenguaje

No totalmente. Solo independiente del idioma con respecto a los idiomas OO. Los patrones de diseño no se aplican a los lenguajes de procedimiento en absoluto. Apenas tienen sentido en un contexto de diseño de base de datos relacional. No se aplican al diseñar una hoja de cálculo.

Es un patrón de diseño típico de OOP y su equivalente funcional?

Lo anterior no debería existir. Es como pedir un código de procedimiento reescrito como código OO. Ummm ... Si traduzco el Fortran original (o C) a Java, no he hecho nada más que traducirlo. Si lo reescribo totalmente en un paradigma OO, ya no se parecerá en nada al Fortran original o C, será irreconocible.

No hay un mapeo simple del diseño OO al diseño funcional. Son formas muy diferentes de ver el problema.

La programación funcional (como todos los estilos de programación) tiene patrones de diseño. Las bases de datos relacionales tienen patrones de diseño, OO tiene patrones de diseño y la programación de procedimientos tiene patrones de diseño. Todo tiene patrones de diseño, incluso la arquitectura de los edificios.

Los patrones de diseño, como concepto, son una forma intemporal de construir, independientemente de la tecnología o el dominio del problema. Sin embargo, los patrones de diseño específicos se aplican a dominios y tecnologías de problemas específicos.

Todos los que piensen en lo que están haciendo descubrirán patrones de diseño.

S.Lott
fuente
12
MVC no es diseño OO. Es diseño arquitectónico, ese patrón se aplica bastante ampliamente.
S.Lott
1
@Princess: la programación funcional no es necesariamente más simple. En tu ejemplo, sí. Por otras cosas, el jurado aún está fuera. Pero ha descartado un patrón de diseño Java OO y ha adoptado un patrón de diseño FP.
S.Lott
1
+1: Prefiero esta respuesta a la respuesta de Jalf anterior. Aunque algunos patrones de diseño abordan deficiencias en el lenguaje, no todos lo hacen. Por ejemplo, difícilmente diría que el patrón de diseño "desatar el nudo recursivo" aborda una deficiencia en el lenguaje, es solo un modismo útil para aflojar las dependencias.
Jon Harrop
99
Java 8 incluirá cierres, también conocidos como funciones anónimas, también conocidas como expresiones lambda. Esto hará que el patrón de diseño de comandos quede obsoleto para Java. Este es un ejemplo de deficiencia del lenguaje, ¿no? Agregaron una característica que falta y ahora no necesita el patrón de diseño.
Todd Chaffee
2
+1 para la oración de cierre. Los patrones de diseño están destinados a simplificar la programación y hacer que los programas resultantes sean más eficientes, en lo que están destinados a hacer.
Clasificador
46

Los comentarios de Brian sobre la estrecha vinculación entre el lenguaje y el patrón son ir al grano,

La parte que falta en esta discusión es el concepto de idioma. El libro de James O. Coplien, "Advanced C ++" fue una gran influencia aquí. Mucho antes de descubrir a Christopher Alexander y la Columna sin nombre (y tampoco se puede hablar con sensatez sobre los patrones sin leer a Alexander), habló sobre la importancia de dominar las expresiones idiomáticas para realmente aprender un idioma. Él usó una copia de cadena en C como ejemplo. while(*from++ = *to++);Puedes ver esto como una curita para una característica de lenguaje que falta (o característica de biblioteca), pero lo que realmente importa es que es una unidad de pensamiento o expresión más grande que cualquiera de sus partes

Eso es lo que los patrones y los lenguajes intentan hacer, para permitirnos expresar nuestras intenciones de manera más sucinta. Cuanto más ricas son las unidades de pensamiento, más complejos son los pensamientos que puedes expresar. Tener un vocabulario rico y compartido en una variedad de escalas, desde la arquitectura del sistema hasta los pequeños retoques, nos permite tener conversaciones más inteligentes y pensamientos sobre lo que deberíamos estar haciendo.

También podemos, como individuos, aprender. Cuál es el punto completo del ejercicio. Todos podemos entender y usar cosas que nunca podríamos pensar de nosotros mismos. Los idiomas, los marcos, las bibliotecas, los patrones, las expresiones idiomáticas, etc., tienen su lugar en compartir la riqueza intelectual.

Peter Mortensen
fuente
8
¡Gracias! de esto se tratan los patrones: "fragmentación conceptual" para reducir la carga cognitiva.
Randall Schulz el
Y las mónadas funcionales definitivamente pertenecen a esta discusión.
Greg
@RandallSchulz: las características del lenguaje (y su uso idiomático, por supuesto) también encajarían bien en la categoría de "fragmentación conceptual para reducir la carga cognitiva".
Roy Tinker
39

El libro GoF se vincula explícitamente a OOP: el título es Patrones de diseño: elementos de software orientado a objetos reutilizables (énfasis mío).

brillante
fuente
26

Aquí hay otro enlace que trata este tema: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/

En su publicación de blog, Edward describe los 23 patrones originales de GoF en términos de Haskell.

folone
fuente
44
El artículo no parece mostrar realmente patrones de diseño en Haskell, pero muestra cómo Haskell aborda esas necesidades sin dichos patrones.
Fresheyeball
3
@Fresheyball: depende de tu definición de patrones. ¿El mapeo de una función sobre una lista es una variante del patrón Visitor? En general, he pensado que la respuesta fue "sí". Se supone que los patrones van más allá de una sintaxis particular. La función que se aplica podría ajustarse como un objeto o pasar como un puntero de función, pero el concepto es el mismo para mí. ¿Estás en desacuerdo?
srm
20

Cuando intente ver esto en el nivel de "patrones de diseño" (en general) y "FP versus OOP", las respuestas que encontrará serán turbias en el mejor de los casos.

Ir un nivel más profundo en ambos ejes, sin embargo, y tener en cuenta los patrones de diseño específicas y características específicas del lenguaje y las cosas se vuelven más claras.

Entonces, por ejemplo, algunos patrones específicos, como Visitante , Estrategia , Comando y Observador definitivamente cambian o desaparecen cuando se usa un lenguaje con tipos de datos algebraicos y coincidencia de patrones , cierres , funciones de primera clase , etc. Algunos otros patrones del libro GoF todavía "quedarse", sin embargo.

En general, diría que, con el tiempo, los patrones específicos están siendo eliminados por las nuevas características del lenguaje (o por el aumento de popularidad). Este es el curso natural del diseño del lenguaje; A medida que los idiomas se vuelven de más alto nivel, las abstracciones que anteriormente solo se podían mencionar en un libro usando ejemplos, ahora se convierten en aplicaciones de una característica o biblioteca de lenguaje en particular.

(Aparte: aquí hay un blog reciente que escribí, que tiene otros enlaces para más discusión sobre FP y patrones de diseño).

Brian
fuente
¿Cómo puedes decir que el patrón de visitante "desaparece"? ¿No pasa de "crear una interfaz de visitante con un montón de métodos de visita" a "usar tipos de unión y coincidencia de patrones"?
Gabe
22
Sí, pero eso cambió de un patrón que es una idea de diseño sobre la que se lee en un libro y se aplica a su código, a "solo código". Es decir, "usar tipos de unión y coincidencia de patrones" es la forma en que normalmente codifica cosas en dicho lenguaje. (Analogía: si ningún idioma tenía forbucles y todos tenían whilebucles, entonces "For" podría ser un patrón de iteración. Pero cuando fores solo una construcción compatible con el lenguaje y cómo las personas codifican normalmente, entonces no es un patrón, usted no ' No necesito un patrón, es solo código, hombre.)
Brian
44
Dicho de otra manera, una prueba de fuego tal vez no está mal para "es un patrón" es: presente el código escrito de esta manera a un estudiante universitario de segundo año que se especializa en CS con un año de experiencia en programación en su idioma. Si les muestra el código y dicen "ese es un diseño inteligente", entonces es un patrón. Si les muestra el código y dicen "¡bueno, duh!", Entonces no es un patrón. (Y si le muestras este "Visitante" a cualquiera que haya hecho ML / F # / Haskell durante un año, irá "bien, ¡duh!")
Brian
1
Brian: Creo que tenemos diferentes definiciones de un "patrón". Considero que cualquier abstracción de diseño identificable es un patrón , mientras que considera que solo las abstracciones no obvias son un patrón . El hecho de que C # tenga foreachy Haskell mapMno significa que no tengan el patrón Iterator. No veo ningún problema al decir que el patrón Iterator se implementa como la interfaz genérica IEnumerable<T>en C # y la clase de tipo Traversableen Haskell.
Gabe
Puede ser que los patrones no obvios sean útiles para los ingenieros de software, pero todos los patrones son útiles para los diseñadores de idiomas. Es decir, "si está creando un nuevo lenguaje, asegúrese de incluir una forma limpia de expresar el patrón iterador". Incluso los patrones obvios son interesantes cuando comenzamos a hacer la pregunta: "¿Existe una mejor sintaxis para expresar esta idea?" Después de todo, eso es lo que lleva a alguien a crear foreach.
srm
16

La presentación de Norvig alude a un análisis que hicieron de todos los patrones GoF, y dicen que 16 de los 23 patrones tenían implementaciones más simples en lenguajes funcionales, o simplemente eran parte del lenguaje. Entonces, presumiblemente, al menos siete de ellos fueron a) igualmente complicados ob) no presentes en el idioma. Desafortunadamente para nosotros, ¡no están enumerados!

Creo que está claro que la mayoría de los patrones "creacionales" o "estructurales" en GoF son meramente trucos para que los sistemas de tipos primitivos en Java o C ++ hagan lo que quieran. Pero el resto es digno de consideración, sin importar en qué idioma programe.

Uno podría ser prototipo; Si bien es una noción fundamental de JavaScript, debe implementarse desde cero en otros idiomas.

Uno de mis patrones favoritos es el patrón de objeto nulo: representa la ausencia de algo como un objeto que no hace nada apropiado. Esto puede ser más fácil de modelar en un lenguaje funcional. Sin embargo, el logro real es el cambio de perspectiva.

Peter Mortensen
fuente
2
Qué análisis extraño hacer ya que los patrones GoF fueron diseñados específicamente para lenguajes OOP basados ​​en clases. Parece un poco como analizar si las llaves de tubo son buenas para realizar trabajos eléctricos.
munificent
@munificent: En realidad no. La orientación a objetos proporciona polimorfismo; La programación funcional generalmente proporciona polimorfismo.
Marcin
@Marcin un programador OO significa algo muy diferente por polimorfismo que un programador funcional.
AndrewC
@ AndrewC No estoy de acuerdo. El programador OO puede pensar que significan algo diferente, pero no lo hacen.
Marcin
3
@Marcin En mi experiencia, un programador de OO generalmente se refiere al polimorfismo de subtipo (a menudo solo usando Object), utilizando modelos para lograrlo, o polimorfismo ad-hoc (sobrecarga, etc.). Cuando un programador funcional dice polimorfismo, se refiere a polimorfismo paramétrico (es decir, funciona para cualquier tipo de datos: Int, función, lista), que quizás se parezca más a la programación genérica de OO que a algo que los programadores de OO generalmente llaman polimorfismo.
AndrewC
15

Diría que cuando tienes un lenguaje como Lisp con soporte para macros, entonces puedes construir tus propias abstracciones específicas de dominio, abstracciones que a menudo son mucho mejores que las soluciones idiomáticas generales.

Anders Rune Jensen
fuente
Estoy completamente perdido Para subir algo con abstracciones ... ¿Qué significa eso?
tuinstoel 01 de
2
Puede construir abstracciones específicas de dominio (incluso las integradas) sin macros. Las macros simplemente te permiten embellecerlas agregando una sintaxis personalizada.
Jon Harrop
2
Puedes pensar en Lisp como un conjunto de Legos para construir lenguajes de programación: es un lenguaje pero también es un metalenguaje. Lo que significa que para cualquier dominio problemático, puede diseñar a medida un lenguaje que no tenga deficiencias obvias. Requerirá algo de práctica, y Kurt Gödel puede estar en desacuerdo, pero vale la pena pasar un tiempo con Lisp para ver qué aporta (tabla, macros).
Greg
9

E incluso las soluciones de patrones de diseño OO son específicas del idioma.

Los patrones de diseño son soluciones a problemas comunes que su lenguaje de programación no resuelve por usted. En Java, el patrón Singleton resuelve el problema de uno de algo (simplificado).

En Scala, tiene una construcción de nivel superior llamada Objeto además de Clase. Está perezosamente instanciado y solo hay uno. No tiene que usar el patrón Singleton para obtener un Singleton. Es parte del lenguaje.

Peter Mortensen
fuente
8

Los patrones son formas de resolver problemas similares que se ven una y otra vez, y luego se describen y documentan. Entonces no, FP no va a reemplazar patrones; sin embargo, FP podría crear nuevos patrones y hacer que algunos patrones actuales de "mejores prácticas" sean "obsoletos".

Edwin Buck
fuente
44
Los patrones de GoP son formas de resolver el problema de las limitaciones de un tipo particular de lenguaje de programación que se interpone en su camino. Por ejemplo, "Quiero indirecta en las clases y decirles que hagan objetos" -> "No puedes, pero puedes hacer objetos similares a metaclases llamados Factory". "Quiero envío múltiple" -> "No puedes, pero hay un laberinto que puedes implementar llamado Patrón de visitante". Etc. Ninguno de los patrones tiene sentido si no estás en un lenguaje OOP con limitaciones específicas.
Kaz
1
No sé si "ninguno" tiene sentido en otros idiomas, pero estoy de acuerdo en que muchos de ellos no tienen sentido en otros idiomas. El adaptador y el puente parecen tener más posibilidades multilingües, disminuyendo un poco para el visitante y quizás un poco menos para el oyente. Sin embargo, los patrones en todos los idiomas siempre sufrirán el tipo de "cómo hacer la operación del lenguaje X en el lenguaje Y" apuntalando los límites naturales del lenguaje. Un ejemplo perfecto fue el patrón Singleton, que es básicamente, ¿cómo obtengo las C globales en OOP? (que responderé, no deberías).
Edwin Buck
1
Segundo Kaz: los patrones no son "una forma de resolver problemas similares que se ven una y otra vez", sino una "forma de resolver problemas similares que se ven una y otra vez Y tienen que reescribirse una y otra vez porque el lenguaje no permite escríbelo solo una vez ". En otras palabras, si el lenguaje permitió extraer / extraer el patrón en la biblioteca / clase / módulo, etc., deja de ser un patrón pero se convierte en una biblioteca / clase / módulo. En FP, es mucho más fácil extraer / abstraer bits de códigos para una función, por lo tanto, los "patrones" se convierten más fácilmente en códigos reutilizables, por lo que no son un patrón.
mb14
1
Su interpretación es bienvenida, pero el libro de GoF fue claro al definir un patrón, y si lee los capítulos introductorios, no dice nada sobre los idiomas o las debilidades del idioma. Ciertamente, algunos idiomas tienen áreas que los harán aprovechar algunos patrones más a menudo, pero ya sea que lo escriba diez veces (cortar y pegar) o lo implemente una vez con diez realizaciones (subclasificación), o tenga un marco configurado para hacerlo diez ligeramente diferentes formas, es solo un detalle de implementación del patrón expuesto.
Edwin Buck
1
Volviendo a esta conversación después de años, creo que mucha gente asocia los patrones con un lenguaje de programación específico o un paradigma de programación específico. Se pueden usar en ese contexto, pero existían antes de la programación. "Una forma intemporal de construir" analiza los patrones en la arquitectura de edificios y la planificación comunitaria. Esto implica que las técnicas orientadas a patrones pueden usarse fuera de las "limitaciones de un lenguaje" a menos que desee llamar a la construcción de edificios un lenguaje de programación :)
Edwin Buck
8

Como otros han dicho, hay patrones específicos para la programación funcional. Creo que la cuestión de deshacerse de los patrones de diseño no es tanto una cuestión de cambiar a funcional, sino una cuestión de características del lenguaje .

Eche un vistazo a cómo Scala elimina el "patrón singleton": simplemente declara un objeto en lugar de una clase. Otra característica, la coincidencia de patrones, ayuda a evitar la confusión del patrón de visitante. Vea la comparación aquí: Coincidencia de patrones de Scala = Patrón de visitante con esteroides

Y Scala, como F #, es una fusión de OO-funcional. No sé acerca de F #, pero probablemente tenga este tipo de características.

Los cierres están presentes en el lenguaje funcional, pero no necesitan restringirse a ellos. Ayudan con el patrón delegador.

Una observación más. Este código implementa un patrón: es un clásico y es tan elemental que generalmente no pensamos en él como un "patrón", pero seguro que es:

for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }

Lenguajes imperativos como Java y C # han adoptado lo que es esencialmente una construcción funcional para tratar esto: "foreach".

Alemán
fuente
Yo diría que Scala incluye soporte de primera clase para el patrón singleton. El patrón todavía está allí, pero el código repetitivo necesario para aplicar el patrón se reduce considerablemente en comparación con Java.
JacquesB
Si las opiniones fueran como una mierda, bueno ... Mira el resto de las respuestas. "simplemente declaras un objeto en lugar de una clase" es tan cierto que explícitamente lo llamaría un objeto literal (es decir, var singleton = {};). También me gusta la mención sobre el patrón foreach. Desafortunadamente, parece que la mayoría de las personas que respondieron / comentaron esta pregunta no entienden la programación funcional y prefieren justificar el uso de patrones de diseño de OOP. +1 por proporcionar ejemplos concretos, daría más si pudiera.
Evan Plaice
@JacquesB No puedo comentar sobre Scala / Haskell, pero en JavaScript (es decir, híbrido funcional / imperativo) no hay absolutamente nada, solo debe ajustar la forma en que declara los objetos mediante el uso de combinaciones de sintaxis literal de objetos, funciones anónimas, funciones de paso como primero miembros de la clase, y permitiendo la herencia múltiple (eliminando la necesidad de contratos de interfaz).
Evan Plaice
8

GoF Design Patterns está codificando soluciones alternativas para lenguajes OO que son descendientes de Simula 67 , como Java y C ++.

La mayoría de los "males" tratados por los patrones de diseño son causados ​​por:

  • clases de tipo estático, que especifican objetos, pero no son objetos en sí mismos;
  • restricción al despacho único (solo el argumento de la izquierda se usa para seleccionar un método, los argumentos restantes se consideran solo tipos estáticos: si tienen tipos dinámicos, depende del método resolverlo con enfoques ad-hoc);
  • distinción entre llamadas a funciones regulares y llamadas a funciones orientadas a objetos, lo que significa que las funciones orientadas a objetos no pueden pasarse como argumentos funcionales donde se esperan funciones regulares y viceversa; y
  • distinción entre "tipos base" y "tipos de clase".

No hay uno solo de estos patrones de diseño que no desaparezca en el Sistema de objetos Common Lisp, a pesar de que la solución está estructurada esencialmente de la misma manera que en el patrón de diseño correspondiente. (Además, ese sistema de objetos precede al libro GoF por más de una década. Common Lisp se convirtió en un estándar ANSI el mismo año en que ese libro se publicó por primera vez).

En lo que respecta a la programación funcional, si los patrones se aplican o no depende de si el lenguaje de programación funcional dado tiene algún tipo de sistema de objetos, y si está modelado según los sistemas de objetos que se benefician de los patrones. Ese tipo de orientación a objetos no se combina bien con la programación funcional, porque la mutación de estado está en el frente y en el centro.

La construcción y el acceso no mutante son compatibles con la programación funcional, por lo que los patrones que tienen que ver con la abstracción del acceso o la construcción podrían ser aplicables: patrones como Factory, Facade, Proxy, Decorator y Visitor.

Por otro lado, los patrones de comportamiento como Estado y Estrategia probablemente no se aplican directamente en la POO funcional porque la mutación del estado es su núcleo. Esto no significa que no se apliquen; quizás de alguna manera se aplican en combinación con cualquier truco disponible para simular el estado mutable.

Kaz
fuente
2
"Los patrones de diseño de GoF están codificando soluciones alternativas" es simplemente una declaración falsa.
John Peters
7

Me gustaría conectar un par de documentos excelentes pero algo densos de Jeremy Gibbons: "Diseñar patrones como programas genéricos de tipo de datos de orden superior" y "La esencia del patrón Iterator" (ambos disponibles aquí: http: // www. comlab.ox.ac.uk/jeremy.gibbons/publications/ ).

Ambos describen cómo las construcciones funcionales idiomáticas cubren el terreno cubierto por patrones de diseño específicos en otros entornos (orientados a objetos).

sclv
fuente
6

No puede tener esta discusión sin mencionar los sistemas de tipos.

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.

Esto se debe a que estas características no abordan los mismos problemas que OOP ... son alternativas a la programación imperativa. La respuesta de FP a OOP se encuentra en los sistemas de tipos de ML y Haskell ... específicamente tipos de suma, tipos de datos abstractos, módulos de ML y clases de tipos de Haskell.

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)

Lo primero que hacen las clases de tipos es eliminar la necesidad de singletons.

Podrías revisar la lista de 23 y eliminar más, pero no tengo tiempo para hacerlo ahora.

Jason Ganetsky
fuente
66
¿Cómo eliminan las clases de tipos (el equivalente de FP de las interfaces OOP) la necesidad de singletons (el equivalente de FP del estado global)?
Gabe
4

Creo que solo dos patrones de diseño de GoF están diseñados para introducir la lógica de programación funcional en el lenguaje OO natural. Pienso en Estrategia y Comando. Algunos de los otros patrones de diseño de GoF pueden modificarse mediante programación funcional para simplificar el diseño y mantener el propósito.

CyaNnOrangeHead
fuente
44
La cuestión es que el punto principal de muchos patrones es aprovechar el polimorfismo para hacer cosas que un soporte decente para los conceptos de PF podría permitir automáticamente. (La mayoría de las encarnaciones que he visto de Builder, por ejemplo, son solo curry a medias). Una vez que puede tratar fácilmente las funciones como valores, los patrones a menudo se simplifican hasta el punto trivial. Se convierten en "pasar una devolución de llamada" o "tienen un diccionario de devoluciones de llamada", y diferentes clases de constructores, por ejemplo, casi pueden desaparecer. En mi opinión, un patrón deja de ser un patrón una vez que es lo suficientemente trivial como para ser exactamente cómo funcionan las cosas , en lugar de algo que necesita implementar.
cHao
4

¡Esencialmente !

  • Cuando un patrón evita las características faltantes (funciones de alto orden, manejo de flujo ...) que finalmente facilitan la composición .
  • La necesidad de reescribir la implementación de patrones una y otra vez puede verse como un olor a lenguaje .

Además, esta página (AreDesignPatternsMissingLanguageFeatures) proporciona una tabla de traducción "patrón / característica" y algunas buenas discusiones, si está dispuesto a excavar.

YvesgereY
fuente
3

La programación funcional no reemplaza los patrones de diseño. Los patrones de diseño no pueden ser reemplazados.

Los patrones simplemente existen; surgieron con el tiempo. El libro de GoF formalizó algunos de ellos. Si los nuevos patrones salen a la luz a medida que los desarrolladores usan lenguajes de programación funcionales, es algo emocionante, y tal vez también se escriban libros sobre ellos.

ktingle
fuente
1
Los patrones de diseño no pueden ser reemplazados? Eso es un poco cerrado, creo. Probablemente todos podamos estar de acuerdo en que los patrones de diseño están destinados a resolver problemas de programación, y al menos me gustaría esperar que algún día podamos resolver esos problemas sin patrones de diseño.
Metrópolis
3
Cualquier patrón particular puede ser reemplazable, pero el concepto de patrones no puede. Recuerde que el término "patrón" surgió en el campo de la arquitectura .
Frank Shearar
1
Los patrones no están destinados a resolver problemas de programación. Los patrones son formas en que programamos. La documentación de los patrones está destinada a ayudar a resolver problemas de programación.
Torbjørn
3
@ Torbjørn: Los patrones son formas en que programamos cuando el idioma se interpone . Existen debido a un desajuste entre el comportamiento deseado del programa y las habilidades integradas del lenguaje, donde los requisitos y las habilidades no se mapean bien o se mapean de manera ambigua. Si no fuera por eso, no habría patrón; tendrías una implementación que es solo cómo se hacen las cosas , y otras implementaciones efectivamente no valdrían la pena considerar.
cHao
1
Excepto que los patrones realmente existen solo para facilitar la comunicación. No hay otro propósito. Y en todas las reuniones de diseño a las que he asistido a lo largo de los años, una discusión sobre el algoritmo es lo importante, no el patrón. El patrón rara vez explicaba lo que realmente estaba sucediendo en un sentido significativo. ¿Explica con precisión los impactos O (n) vs O (n Log (n))? No. ¿Explica con qué facilidad se ajustará a la arquitectura existente? No. Las discusiones sobre algoritmos a gran escala sí. No estoy argumentando que los patrones deberían retirarse per se, pero si lo fueran, casi nada sufriría como resultado.
3

En el nuevo libro de 2013 llamado "Patrones de programación funcional- en Scala y Clojure", el autor Michael.B. Linn hace un trabajo decente al comparar y proporcionar reemplazos en muchos casos para los patrones GoF y también analiza los patrones funcionales más nuevos como 'recursión de cola', 'memorización', 'secuencia perezosa', etc.

Este libro está disponible en Amazon. Lo encontré muy informativo y alentador cuando provenía de un entorno de OO de un par de décadas.

viril
fuente
3

OOP y los patrones de GoF tratan con los estados. OOP modela la realidad para mantener la base del código lo más cerca posible de los requisitos dados de la realidad. Los patrones de diseño de GoF son patrones que se identificaron para resolver problemas atómicos del mundo real. Manejan el problema del estado de manera semántica.

Como en la programación funcional real no existe ningún estado, no tiene sentido aplicar los patrones GoF. No hay patrones de diseño funcionales de la misma manera que hay patrones de diseño GoF. Cada patrón de diseño funcional es artificial en contraste con la realidad, ya que las funciones son construcciones de las matemáticas y no de la realidad.

Las funciones carecen del concepto de tiempo, ya que siempre devuelven el mismo valor independientemente de la hora actual, a menos que el tiempo sea parte de los parámetros de la función, lo que hace que sea realmente difícil procesar las "solicitudes futuras". Los lenguajes híbridos mezclan esos conceptos y hacen que los lenguajes no sean lenguajes de programación funcionales reales.

Los lenguajes funcionales están aumentando solo por una cosa: las restricciones naturales actuales de la física. Los procesadores de hoy están limitados en su velocidad de procesamiento de instrucciones debido a las leyes físicas. Usted ve un estancamiento en la frecuencia del reloj pero una expansión en los núcleos de procesamiento. Es por eso que el paralelismo de las instrucciones se vuelve cada vez más importante para aumentar la velocidad de las aplicaciones modernas. Como la programación funcional por definición no tiene estado y, por lo tanto, no tiene efectos secundarios, es seguro procesar funciones de forma segura en paralelo.

Los patrones de GoF no son obsoletos. Son al menos necesarios para modelar los requisitos del mundo real. Pero si usa un lenguaje de programación funcional, debe transformarlos en sus equivalentes híbridos. Finalmente, no tiene la oportunidad de crear solo programas funcionales si usa persistencia. Para los elementos híbridos de su programa, sigue existiendo la necesidad de utilizar patrones GoF. Para cualquier otro elemento que sea puramente funcional no hay necesidad de usar patrones GoF porque no hay estado.

Debido a que los patrones de GoF no son necesarios para la programación funcional real, no significa que no se apliquen los principios SOLID. Los principios SOLID están más allá de cualquier paradigma de lenguaje.

oopexpert
fuente
2
FP puede tener estado, simplemente no hay estado global, compartido o mutable.
vt5491
2

En la programación funcional, los patrones de diseño tienen un significado diferente. De hecho, la mayoría de los patrones de diseño de OOP son innecesarios en la programación funcional debido al mayor nivel de abstracción y HOFs utilizados como bloques de construcción.

El principio de un HOF significa que las funciones se pueden pasar como argumentos a otras funciones. y las funciones pueden devolver valores.

Ali Bayat
fuente
1

Como decía la respuesta aceptada, OOP y FP tienen sus patrones específicos.

Sin embargo, hay algunos patrones que son tan comunes que todas las plataformas de programación que se me ocurran deberían tener. Aquí hay una lista (incompleta):

  • Adaptador. Apenas puedo pensar en una plataforma de programación útil que sea tan completa (y autocumplida) que no necesite hablar con el mundo. Si va a hacerlo, definitivamente se necesita un adaptador.

  • Fachada. Cualquier plataforma de programación que pueda manejar código fuente grande debería poder modularizarse. Si fuera a crear un módulo para otras partes del programa, querrá ocultar las partes "sucias" del código y darle una interfaz agradable.

  • Interprete. En general, cualquier programa solo está haciendo dos cosas: analizar entrada y salida de impresión. Las entradas del mouse deben analizarse y los widgets de ventana deben imprimirse. Por lo tanto, tener un intérprete incorporado le da al programa poder adicional para personalizar las cosas.

Además, noté en un lenguaje típico de FP, Haskell, que hay algo similar a los patrones GoF, pero con diferentes nombres. En mi opinión, esto sugiere que estaban allí porque hay algunos problemas comunes que resolver en los lenguajes FP y OOP.

  • Transformador de mónada y decorador. El primero solía agregar habilidad adicional en una mónada existente, el segundo agrega habilidad adicional a un objeto existente.
Motor de tierra
fuente
1

Creo que cada paradigma tiene un propósito diferente y, como tal, no se puede comparar de esta manera.

No he oído que los patrones de diseño de GoF sean aplicables a todos los idiomas. He oído que son aplicables a todos los idiomas OOP . Si usa programación funcional, entonces el dominio de los problemas que resuelve es diferente de los lenguajes OO.

No usaría un lenguaje funcional para escribir una interfaz de usuario, pero uno de los lenguajes OO como C # o Java facilitaría este trabajo. Si estuviera escribiendo un lenguaje funcional, entonces no consideraría usar patrones de diseño OO.

Vincent Ramdhanie
fuente
1

OOP y FP tienen diferentes objetivos. OOP tiene como objetivo encapsular las complejidades / partes móviles de los componentes de software y FP pretende minimizar la complejidad y las dependencias de los componentes de software.

Sin embargo, estos dos paradigmas no son necesariamente 100% contradictorios y podrían aplicarse juntos para obtener el beneficio de ambos mundos.

Incluso con un lenguaje que no admite de forma nativa la programación funcional como C #, podría escribir código funcional si comprende los principios de FP. Del mismo modo, podría aplicar los principios de OOP utilizando F # si comprende los principios, patrones y mejores prácticas de OOP. Tomaría la decisión correcta en función de la situación y el problema que intente resolver, independientemente del lenguaje de programación que utilice.

Dogu Arslan
fuente
1

Algunos patrones son más fáciles de implementar en un lenguaje compatible con FP. Por ejemplo, la estrategia se puede implementar usando muy bien los cierres. Sin embargo, dependiendo del contexto, es posible que prefiera implementar la Estrategia utilizando un enfoque basado en la clase, por ejemplo, donde las estrategias en sí son bastante complicadas y / o comparten la estructura que desea modelar utilizando el Método de plantilla.

En mi experiencia de desarrollo en un lenguaje de paradigmas múltiples (Ruby), la implementación de FP funciona bien en casos simples, pero cuando el contexto es más complicado, el enfoque basado en GoF OOP se ajusta mejor.

El enfoque FP no reemplaza el enfoque OOP, sino que lo complementa.

ComDubh
fuente
0

La característica primordial de la programación funcional, en mi humilde opinión, es que está programando con nada más que expresiones : expresiones dentro de expresiones dentro de expresiones que evalúan a la última expresión final que "calienta la máquina cuando se evalúa".

La característica primordial de la programación orientada a objetos, en mi humilde opinión, es que está programando con objetos que tienen estado interno. No puede tener un estado interno en funciones puras: los lenguajes de programación orientados a objetos necesitan declaraciones para que las cosas sucedan. (No hay declaraciones en la programación funcional).

Estas comparando manzanas con naranjas. Los patrones de programación orientada a objetos no se aplican a la programación de funciones, porque la programación funcional es programación con expresiones, y la programación orientada a objetos es programación con estado interno.

Jim Flood
fuente
Hmm, debería haber notado que la pregunta tenía once años antes de responder. :-)
Jim Flood
0

Prepárense.

A muchos les agradará escucharme decir que he reemplazado los patrones de diseño y desacreditado SOLID y DRY. No soy nadie. Sin embargo, modelé correctamente la arquitectura colaborativa (fabricación) y publiqué las reglas para crear procesos en línea junto con el código y la ciencia detrás de esto en mi sitio web http://www.powersemantics.com/ .

Mi argumento es que los patrones de diseño intentan lograr lo que la fabricación llama "personalización masiva", una forma de proceso en la cual cada paso puede ser reformado, recompuesto y extendido. Puede pensar en tales procesos como scripts sin compilar. No voy a repetir mi argumento (en línea) aquí. En resumen, mi arquitectura de personalización masiva reemplaza los patrones de diseño al lograr esa flexibilidad sin ninguna de las semánticas desordenadas. Me sorprendió que mi modelo funcionara tan bien, pero la forma en que los programadores escriben el código simplemente no oculta cómo la fabricación organiza el trabajo colaborativo.

  • Fabricación = cada paso interactúa con un producto
  • OOP = cada paso interactúa consigo mismo y con otros módulos, pasando el producto de un punto a otro como empleados de oficina inútiles

Esta arquitectura nunca necesita refactorización. También hay reglas sobre centralización y distribución que afectan la complejidad. Pero para responder a su pregunta, la programación funcional es otro conjunto de semántica de procesamiento, no una arquitectura para procesos personalizados masivos donde 1) el enrutamiento de origen existe como un documento (script) que el usuario puede reescribir antes de disparar y 2) los módulos pueden ser fácilmente agregado o eliminado dinámicamente.

Podríamos decir que OOP es el paradigma del "proceso codificado" y que los patrones de diseño son formas de evitar ese paradigma. Pero de eso se trata la personalización masiva. Los patrones de diseño incorporan procesos dinámicos como código duro desordenado. Simplemente no tiene sentido. El hecho de que F # permita pasar funciones como parámetro significa que los lenguajes funcionales y OOP intentan lograr la personalización masiva.

¿Qué tan confuso es eso para el lector, el código duro que representa el script? En absoluto si cree que los consumidores de su compilador pagan por tales funciones, pero para mí esas funciones son un desperdicio semántico. No tienen sentido, porque el objetivo de la personalización masiva es hacer que los procesos sean dinámicos , no solo dinámicos para el programador que maneja Visual Studio.

RBJ
fuente
0

Lo hace, ya que un PL funcional de alto nivel (como OCaml, con clases, módulos, etc.) ciertamente reemplaza a los lenguajes imperativos OOP en versatilidad de tipo y poder de expresión. Las abstracciones no se filtran, puede expresar la mayoría de sus ideas directamente en el programa. Por lo tanto, sí, reemplaza los patrones de diseño, la mayoría de los cuales son ridículamente simplistas en comparación con los patrones funcionales de todos modos.

exa
fuente