En los últimos años, los idiomas que me gusta usar se están volviendo cada vez más "funcionales". Ahora uso lenguajes que son una especie de "híbrido": C #, F #, Scala. Me gusta diseñar mi aplicación usando clases que corresponden a los objetos del dominio, y usar características funcionales donde esto hace que la codificación sea más fácil, más coincidente y más segura (especialmente cuando se opera en colecciones o cuando se pasan funciones).
Sin embargo, los dos mundos "chocan" cuando se trata de diseñar patrones. El ejemplo específico que enfrenté recientemente es el patrón Observador. Quiero que un productor notifique algún otro código (los "consumidores / observadores", digamos un almacenamiento de base de datos, un registrador, etc.) cuando se crea o cambia un elemento.
Inicialmente lo hice "funcionalmente" así:
producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed
Pero ahora me pregunto si debería usar un enfoque más "OO":
interface IItemObserver {
onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...
producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop
¿Cuáles son las ventajas y desventajas de los dos enfoques? Una vez escuché a un gurú de FP decir que los patrones de diseño están ahí solo por las limitaciones del lenguaje, y es por eso que hay tan pocos en los lenguajes funcionales. Tal vez esto podría ser un ejemplo de ello?
EDITAR: En mi caso particular, no lo necesito, pero ... ¿cómo implementaría la eliminación y la adición de "observadores" de manera funcional? (Es decir, ¿cómo implementaría todas las funcionalidades en el patrón?) ¿Simplemente pasando una nueva función, por ejemplo?
fuente
Respuestas:
Ese es un buen ejemplo de dos enfoques diferentes que llevan la noción de realizar una tarea fuera del límite de interés para el objeto que llama.
Si bien está claro en este ejemplo que debe optar por el enfoque funcional, en general dependerá realmente de cuán complejo sea el comportamiento que debe exhibir el objeto llamado. Si realmente se trata de un comportamiento complejo, donde se encontrará reaplicando lógica similar a menudo, y los generadores de funciones no se pueden usar para expresarlo claramente, entonces probablemente querrá ir a la composición o herencia de la clase, donde tener un poco más de libertad para reutilizar y ampliar el comportamiento existente de forma ad-hoc.
Sin embargo, un patrón que sí observé es que, por lo general, los desarrolladores adoptan el enfoque funcional inicialmente y solo una vez que surge la demanda de un comportamiento más granular, deciden optar por un enfoque basado en la clase. Sé, por ejemplo, que Django pasó de vistas basadas en funciones, cargadores de plantillas y corredores de pruebas a las basadas en clases una vez que los beneficios y requisitos se hicieron evidentes, pero no antes de eso.
fuente
La versión funcional es mucho más corta, más fácil de mantener, más fácil de leer y, en general, muy superior en casi todos los aspectos imaginables.
Muchos, aunque lejos de todos, los patrones son para compensar la falta de características en POO, como los Observadores. Esos están mucho mejor modelados funcionalmente.
fuente
Su "FP guru" tiene razón en parte; Muchos patrones OO son hacks para hacer cosas funcionales. (Su afirmación de que esta es la razón por la que hay pocos lenguajes FP parece dudoso en el mejor de los casos). Los patrones de Observador y Estrategia están tratando de emular funciones de primera clase. El patrón de visitante es un truco para simular la coincidencia de patrones. Tu
IItemObserver
es solo una función disfrazada. Pretender que es diferente de cualquier otra función que tome un artículo no te compra nada.Los objetos son solo un tipo de abstracción de datos. Este documento ayudará a arrojar algo de luz sobre ese tema. Los objetos pueden ser útiles, pero es importante reconocer que no son apropiados para todo. No hay dicotomía; simplemente es cuestión de elegir la herramienta adecuada para el trabajo correcto, y la programación funcional de ninguna manera requiere que renuncies a los objetos. Aparte de eso, la programación funcional es más que solo usar funciones. También se trata de minimizar los efectos secundarios y la mutación.
fuente
Realmente no puedo responder la pregunta porque no soy bueno en lenguajes funcionales; pero creo que deberías estar menos preocupado por el enfoque siempre que lo que tienes funcione. Por lo que entiendo, mientras no agregue más oyentes en el futuro o no cambie los oyentes durante la ejecución, puede omitir el patrón de Observador aquí.
No estoy de acuerdo con que los patrones de diseño compensen las "limitaciones" en los lenguajes OO. Están ahí para hacer un buen uso de las características de OOP. El polimorfismo y la herencia son características, no limitaciones. Los patrones de diseño hacen uso de estas características para promover un diseño flexible. Puede hacer un programa absolutamente no OO en un OO. Puede, con mucho cuidado, escribir un programa completo que contenga objetos que no tengan estado, imitando FP.
fuente