Buscar
Social
Ofertas laborales ES
« Google va a cesar más servicios | Main | ¿De dónde viene el nombre de Tomcat? ¿Y Androide? ¿Y Spring? ¿Y...? »
miércoles
mar132013

4 cosas que los programadores Java podemos aprender de Clojure sin aprender Clojure 

Eric Normand ha escrito en su blog una entrada titulada 4 cosas que los programadores Java podemos aprender de Clojure sin aprender Clojure.  Clojure es un lenguaje de programación inspirado en Lisp que compila a bytecode y se ejecuta en la máquina virtual Java.

En esta entrada Eric repasa varias buenas prácticas de programación que Clojure "fuerza" al programador a seguir, pero que también se pueden emplear desde Java. Además, en su blog proporciona referencias y videos que permiten al lector profundizar más en cada una de estas buenas prácticas. Las buenas prácticas son:

  1. Usar objetos inmutables. Sobre ésta, creo que no hace falta dar muchas explicaciones.
  2. No hacer trabajo en el constructor, los constructores deben de ser sencillos. Un constructor complicado tiene habitualmente como consecuencia que la clase en cuestión tenga múltiples responsabilidades. Si es complejo construir un objeto deberíamos crear un método factoría para construirlo. Esto también va a redundar en la testabilidad de nuestro código.
  3. Programar empleando interfaces simples, y no implementaciones concretas de dichas interfaces. De ese modo nuestro código va a ser más genérico y fácil de extender/modificar en el futuro.
  4. Representa computación, y no el mundo real. Intentar modelar en nuestro programa directamente el mundo real no siempre es la mejor aproximación; a veces debemos orientar más el modelado hacia la computación que queremos realizar en nuestro programa con esos objetos del mundo real.

¿Qué opinais de estas buenas prácticas que Eric recomienda?

 

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (24)

Pues yo si necesito explicaciones sobre lo de los objetos inmutables. Si se toma como norma crear objetos sin setters ¿que hacen las aplicaciones? ¿Cuantos tipos de aplicaciones se pueden hacer sin usar listas o vectores? Alguien que me matice esto, por favor.

Cada vez que veo una lista de "las X cosas que tienes que hacer para ser un buen programador" me preparo para leer una lista de verdades categoricas escritas en piedra, sin matices por ninguna parte, y todas dedicadas a cargarse lo que sabemos (y ha funcionado unos cuantos años) de programacion orientada a objetos y a considerar que los programadores son unos imbeciles incapaces de lidiar con la concurrencia, o con la gestion de la memoria... y que ademas siempre olvidan en que variable dejan sus valores, asi que hay que protegerlos de si mismos. Si el lenguaje no te capa lo suficiente, te tienes que capar tu.

marzo 13, 2013 | Registered Commenternilojg

No sé qué tiene que ver Clojure en todo esto, porque no conozco Clojure pero juraría que es un lenguaje funcional, mientras que las recomendaciones son típicas de un diseño orientado a objetos (?).

En cualquier caso el artículo está bastante bien y en general las recomendaciones son muy buenas, no como "reglas de oro" pero sí como indicadores de que puedes estar haciendo algo mal.

marzo 13, 2013 | Unregistered CommenterAndrés Viedma

nilojg, se entiende que lo de los objetos inmutables es "cuando se pueda", y aplicable fundamentalmente a relaciones de composición. Es decir, si tienes un objeto que "contiene" otros a menudo es bueno intentar evitar en la medida de lo posible que se modifiquen esos otros directamente, ya que al hacerlo el objeto padre no se enteraría.

Esto es hasta cierto punto, claro. Por ejemplo, se puede hacer perfectamente un mecanismo con el que cuando se modifique un objeto hijo se le notifique al padre. Y en otras ocasiones se puede entender que el objeto hijo tiene "su propia vida" y no pasa nada porque el padre no se entere. O simplemente, no hacemos nada por simplificar.

La solución ideal depende del caso concreto en el que estemos. El caso más típico de esto es para clases sencillas con valores atómicos (el ejemplo de Date, por ejemplo).

En cualquier caso, esta recomendación en concreto no se puede tomar como norma ni de coña.

marzo 13, 2013 | Unregistered CommenterAndrés Viedma

Lo de usar objetos inmutables se refiere a aprender a trabajar con objetos que no permiten ser modificados.

Para cambiar el atributo "nombre" de un usuario, uno no cambia el String asociado a ese atributo, lo que ocurre realmente es que se crea un nuevo String y se actualiza la referencia en el atributo "nombre" (en lenguajes como C, tienes acceso a la dirección de memoria donde se encuentran las letras del nombre y las puedes modificar in-situ).

Para crear una fecha (eg. fecha de entrega de un pedido), lo se sugiere en el artículo es crear una nueva fecha, probablemente basada en la anterior.

Se sugiere utilizar colecciones que no se pueden modificar para evitar el típico ConcurrentModificationException que se produce al modificar una colección mientras se recorre, y para "agregar" objetos debes crear una nueva colección (lista o diccionario, etc.) indicando que quieres todos los objetos de la versión antigua y uno o más elementos nuevos.

marzo 13, 2013 | Unregistered CommenterDenis Fuenzalida

Las listas y estructuras similares también pueden ser inmutables. De hecho creo que las de Scala lo son (o al menos tiene un tipo que lo es). Lo que hace es cada vez que modificas la lista es sacarle una copia. Y este proceso puede ser mucho más eficiente de lo que pudiera parecer, ya que las múltiples copias de la lista pueden compartir gran cantidad de elementos, de tal modo que "copiar" la lista apenas requiera hacer trabajo.

marzo 13, 2013 | Registered CommenterAbraham

"Classes should be immutable unless there's a very good reason to make them mutable....If a class cannot be made immutable, limit its mutability as much as possible."

Esto lo decía Joshua Bloch en Efecctive Java, que mira que tiene años. Si se trata de "value objects" entonces lo normal es que siempre se haga inmutable, si se trata de un Entity, objeto con "identidad", entonces no es tan sencillo, si alguien referencia a un objeto creado (por ejemplo, el alumno referencia a sus profesores) si cambio los datos de un profesor no puedo crear una nueva instancia y quedarme tan ancho porque los alumnos quedarían referenciando una "copia antigua". Hablo de "value objects" y "Entitys" siguiendo la idea de Domain Driven Design (http://devlicio.us/blogs/casey/archive/2009/02/13/ddd-entities-and-value-objects.aspx), no confundir con estas cosas que en algunos frameworks se llaman "entidades" y no son más que estructuras de datos llenas de getters&setters.

Que ya que estamos, que un objeto sea mutable no implica que tenga "getters", en absoluto, de echo los getters son basicamente saltarse a la torera la idea de diseño más importante de la OO, la ocultación de información o encapsulación. Recomiendo este articulo de Allen hollub (http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html) con un titulo bastante "tendencioso" pero con un contenido con el que estoy bastante de acuerdo (no mucho con algunas soluciones que propone, pero si con la idea general). Ya puestos echarle un ojo al libro de este hombre "holub on patterns" que da una visión de la OO más purista que la tipica que se ve en el mundo java, por lo menos para ver que hay gente que hace OO de otra forma (bueno, en realidad la otra forma es la de java, la de holub es mucho más cercana a la de bertrand meyer y a la del mundo smalltalk, que son las raices de la OO).

Las otras 3 recomendaciones tampoco son nada nuevo, la de los constructores es bastante obvia, sólo usarlos para asignación (nada peor que un constructor haciendo trabajo de más y disparando excepciones...). la tercera es lo mismo que el principio "interface segregation" que es uno de los principios SOLID. Con la cuarta estoy bastante de acuerdo también, tiene mucho que ver con pensar en "roles" en lugar de pensar en objetos que representan al mundo real. Me recuerda a algunas ideas de Data Context Interaction (otro para leer, Lean software architecture de Jim Copliend, muy recomendable con un enfoque de la OO de nuevo bastante diferente, basado en objetos que reciben capacidades/roles, via mixins por ejemplo, en tiempo de ejecución en función del contexto donde se estén ejecutando, muy chulo el planteamiento).

marzo 13, 2013 | Registered Commenteralfredocasado

Hola,

lo de objetos inmutables entre otras cosas es sobretodo para:

- favorecer concurrencia, escalabilidad: te olvidas de líos entre hilos, nunca hay problemas de sincronismo, etc.

- favorecer el testeo de una clase

aunque al principio es un poco raruno, te obliga a pensar más tu código, que suele quedar bastante mejor (más "testeable", más conciso, más fácil de seguir).

Iniciarse en esta técnica es muy sencillo, sólo hay que atizarle un final a los parámetros del método, a las variables de clase y dentro de los métodos O:-D Y pensar para que aún así el programa funcione y permita modificar datos (=copias de instancias de objetos)

Los frameworks IOC -Spring, guice- en tanto que permiten inyectar en constructores, también se pueden adaptar a esta inmutabilidad. De hecho, p.ej., en Spring los beans se tratan por defecto como singletons (no exactamente, pero en general solo hay una instancia de un bean), lo cual sugiere cierta afinidad con la inmutabilidad.

salu2,
perry

marzo 14, 2013 | Unregistered CommenterPerry Mason

Vamos al tema:

* Usar objetos Inmutables:

Estoy con niloj y ciertas tendencias de tratar al programador como un niño irresponsable al que hay que caparle para que no se toque.

Uno de los "grandes" incentivos de Java fue que capaba bastante su padre intelectual que fundamentalmente era C++.

Pero la realidad es tozuda, muy tozuda:

- La String inmutable => pero también necesitamos StringBuffer/StringBuilder mutables
- synchronized soldado en el lenguaje es más que suficiente en vez del uso de APIs en otros lenguajes => ahora disponemos de APIs para hacer lo mismo y más con más control.

- IO monohilo bloqueante para qué complicarse la vida => ahora tenemos también la opción de IO no bloqueante (NIO) y quizás son también APIs multihilo pero eso no lo tengo claro.

- no acceso a memoria de forma directa y que el GC la gestione, niño eso no se toca => ahora tenemos acceso directo con direct buffers y gestión manual.

- no sobrecarga de operadores => sobrecarga de operadores en Scala
- no herencia múltiple => traits en Scala (es herencia múltiple capada pero cerca)

¿Que Date sea inmutable? me da igual, para mi Date es un long y nunca uso los métodos de Date porque son erróneos salvo el setTime.

Lo que necesito es un DateInmutable y un DateMutable y no sólo uno inmutable. Echo mucho mucho de menos un IntegerMutable, un LongMutable etc por ejemplo para pasar enteros como parámetros y devolver un resultado por el parámetro, al final acabo pasando new int[1] y cosas similarmente patéticas.

Pero el ejemplo de Date es muy malo porque como decía para mi no es más que un objeto contenedor de un integer mutable, uso Date para poco más que por identificar que es una fecha como tipo de dato.

* No hacer trabajo en el constructor

Pues depende, precisamente hacer cosas en el constructor es fundamental para conseguir la mayor encapsulación y evitar setX y en general métodos de inicialización que cambian el estado.

Pero el artículo no va por ahí:

"But wait! What is the responsibility of the Person class? Originally, it was "represent personal information about a person". Now it is also responsible for:
Parsing JSON
Making web requests
Reading files
Handling errors"

Pues no, ese tipo de cosas las encontrará el autor en programadores que siguen pensando en POO con estilo de los 80 (si es que verdaderamente piensan en un estilo de POO porque a lo mejor no es más que programación "al tun tun") y quizás en los que defienden los modelos de datos ricos. El problema de esa práctica va mucho más allá que hacer cosas en el constructor.

En mi opinión es esa crítica barata a la programación orientada a objetos la que me suele cabrear y mucho del mundo funcional, porque es mala, infántil, porque parte del falso hecho de que todo el mundo que hace POO, más allá de singleton programming de dominio rico, coge la clase Persona y mete ahí TODAS las funcionalidades correspondientes a la persona, y la verdad es que ese tipo de código apenas se puede encontrar en el típico tutorial de "introducción a la programación orientada a objetos" que en ese contexto está muy bien. A veces pienso que o hay mala fe y poca honestidad en quien utiliza ese tipo de argumentación para defender el mundo funcional o no ha hecho un DAO en su vida.

Que haya lenguajes que se corten las manos "para no pecar" y que critican a los que las tienen, allá ellos yo prefiero tener manos para muchas cosas (y también para pecar un poco gracias).

* Programar empleando interfaces simples

¿Algo nuevo bajo el sol? Aunque ciertas ideas llevadas a la práctica sistemáticamente "porque sí" suelen dar lugar a cientos de interfaces... con una única implementación ¿os suena familiar?.

Lo interesante de esto es que asentimos con facilidad en plan "que razón que razón" y yo en buena parte estoy de acuerdo (mientras que no sea absurda sobre-ingeniería), pero lo sorprendente de esto es que prácticamente NUNCA he oido a alguien criticar a los CIENTOS de toolkits Java que exponen clases mondas y lirondas al programador, más aun he llegado a ver alguno en donde prácticamente todos los métodos eran estáticos. Piensa en cualquiera, lo más probable es que accedas via clases a su API y no via interfaces. A mi me resulta sorprendente lo que me da la impresión de que hay mucha veguenza reconocer que el mundo real... suele ser bastante diferente al mundo de los gurus.

De herencia de implementación no hablamos porque Clojure no tiene y "por tanto es mala", aunque a mi me salva la vida todos todos todos los días y muchos toolkits que usas todos los días también y a saco aunque no te lo cuenten porque parece que está mal visto.

* Representa computación, y no el mundo real.

No gracias, los 70 produjeron muy buena música, Los Beatles, Bowie y el lenguaje C, pero yo prefiero representar LAS DOS COSAS, en mis classes, el mundo real y lo que hago con el mundo real. En el mundo real de la POO hay clases que son muy de estado y clases que son muy funcionales.

"I think my professor was wrong"

No no estaba equivocado, invitaría al autor a darse una vuelta por la API Java y el código fuente de Android para ver que el mundo real sigue pareciéndose mucho mucho a lo que enseñaba su profesor.

En serio es un argumento que repito siempre, me gustaría ver algo como un toolkit GUI hecho con un lenguaje funcional y percibir que no apeste a C. Nota: cualquier UI es un monumento a la gestión de estado, los ejemplos funcionales basados en fibonacci están muy bien pero el dichoso resultado hay que guardarlo en alguna parte ... y gestionarlo.

---------------

Volviendo a la inmutabilidad yo echo de menos paso de parámetros por referencia (pasar un new T[1] como parámetro es patético) u objetos-valor como variables, que funcione algo como

Integer valor(5);

Eso conllevaría que el get fuera:

public int getValor() { return valor; }

que necesariamente devuelve una copia

o bien algo como:

public Integer getValor() { return valor; }

que por razones de "soldadura" tendría que ser una copia, esto permitiría que incluso con Integer mutable devolvieramos forzadamente una copia gracias a la forma de declarar el atributo.

O bien siguiendo un estilo C++

public Integer& getValor() { return valor; }

que devolviera una referencia, tal que si Integer fuera mutable podría modificar el contenido.

Lo mismo valdría para los parámetros (paso por valor) que permitiría pasar por valor (copia) objetos mutables.

En vez de la forma normal:

final Integer valor = new Integer(5);

Esto está ya inventado desde los tiempos del C y C++ (aunque el constructor de copia es de C++) pero joer parece que soy el único en el mundo que defiende algo tan tonto sin tener que cambiarte de lenguaje a algo capado.

marzo 14, 2013 | Registered Commenterjmarranz

Pues a mi no me queda claro porque se supone que estas "4 cosas" son de Clojure, y no estuvieran presentes desde siempre en Java, C++ o cualquier lenguaje OO.
Las 1, 2 y 3 se refieren a Encapsulación, Cohesión y cosas por el estilo. Ahora bien, tal como dice @jmarranz el mundo real es como es, y no como dicen los libros, por ejemplo JPA, exige que una clase @Entity sea un pojo, y como tal adiós encapsulación e inmutabilidad (o sea para mi es, por lo menos, "imprudente" permitir hacer un set de un campo que es PK, pero se podría, y es responsabilidad del programador no dejar el objeto en un estado inconsistente).

La 4 tampoco es nueva, de alguna manera me recuerda a la clasificación de clases por su comportamiento (que no recuerdo como se llama, ni donde lo leí): "entities", "actions" y "boundaries". Lo que creo entender es que, uno se debería concentrar en los actions, más que en los entities y boundaries, lo que de alguna manera no termina de convencerme, pero a alguien le podría funcionar.

marzo 14, 2013 | Unregistered CommenterK'

estoy totalmente de acuerdo con jmarraz en

si es que verdaderamente piensan en un estilo de POO porque a lo mejor no es más que programación "al tun tun"

casi nadie sabe trabajar oo es un mito en la practica todos se ponen a crear sus clase persona, producto etc pero al final las tienen de adorno nunca las usan y se nota su incapacidad de entender la oo realmente no tiene sentido crear este tipo de clases si ninguna tiene métodos públicos y solo la usan para pasar datos

y lo mas gracioso es que tienen la osadía de poner a todas sus clases getters y setters equal hashcode toString y constructor con toda la combinación de parámetros "para seguir las buenas practicas" pero nunca los llaman

y la muestra mas grande de que no entienden el paradigma es que tienen seters para propiedades que son de solo lectura

public class Persona{
private String nombre;
private String apellido;
private String nombreCompleto;

public getNombre(){..}
public setNombre(){..}
public getApellido(){..}
public setApellido(String apellido){
nombreCompleto=nombre+apellido;
conectaABaseDeDatos();
acutalisaNombreCompleto();
try{
peligro();
}catch(Exception e){
e.printStrake();//siempre usan lo primero que el ide les dicte evitan pensar a toda costa
}
}
public getNombreCompleto(){
return nombre+apellido
}

//luego escriben el toString hashCode equals constructor usando el ide pero sin saber lo que hacen
}

yo pienso que al menos sean sinceros y digan mejor no uso el paradigma por que soy incapaz de usarlo. es mucho mejor reconocer que no sabes algo que usarlo mal y estorbar a todo el mundo y decir que entienden algo que realmente no entienden

Pienso que la mayoría no esta listo para la oo los que la entienden son pocos. Deberían tener sus clases todos con metodos publicos al menos son sinceros y no crean código que no van a usar

class Persona{

public String nombre;
public String apellido;

}

se que no es lo mejor ni mas elegante pero en la mayoría de casos como casi nadie entiende la oo da igual al menos son sinceros en reconocerlo
Ademas no todos los proyectos son tan complejos como para tener un modelo rico bien orientado a objetos en la practica solo basta con hacer todo en la base de datos que es como siempre lo han echo

marzo 14, 2013 | Unregistered Commenterlucho

@k

"Pues a mí no me queda claro porque se supone que estas "4 cosas" son de Clojure, y no estuvieran presentes desde siempre en Java, C++ o cualquier lenguaje OO."

Exacto mira te recuerdo que las computadoras fueron creadas hace poquísimo tiempo prácticamente nada yo soy joven por lo que para mí todo el royo de la computación siempre estuvo tan presente como que el mismo sol. Pero eso solo es una impresión falsa; las computadoras tuvieron un inicio muy reciente. Y para los de mayor edad tuvieron la grandiosa suerte de ver la creación de las primeras computadoras e incluso su evolución y supieron lo que era vivir antes y después de las computadoras (una de las más grandes aventuras que nunca pude disfrutas y que tuvieron el privilegio los más viejos).

Lo que quiero decir con todo el párrafo anterior es que esto es nuevo y nunca tuvimos la oportunidad de crear grandes revoluciones todo ya se creó hace 30 o 40 años y luego no se volvió a crear nada lo único que estamos haciendo es crea nuevas y mejores herramientas corrigiendo los errores del anterior(pero en esencia no creando nada) todo lo que se refiere a programación tiene menos de 80 años lo cual no es absolutamente nada a comparación a las otras ciencias que tienen miles de años que ellos si crean cosas realmente revolucionarias y novedosas muchísimas veces.

Todos los lenguajes recién creados no son nada novedosos solo son herramientas que mejorar ciertos excesos de sus predecesores pero no crean nada realmente novedosos. la orientación a objetos por ejemplo fue creada hace mucho tiempo por simula en los 60 los aspectos también son viejos la funcional desde lisp nada novedoso.

Digamos que sale un nuevo lenguaje super cooll(se crean una o dos veces por semana) bueno pues lo único que haría es mirar a su predecesor revelarse y ver sus error y dificultadas y decir que no quiero hacer lo mismo y aquí viene el gran detalle; sigue usando los mismo paradigmas de toda la vida no crean nada y luego hacen lo mismo ha pero eso si lo único que hacen es crear más azúcar sintáctica para hacer lo mismo que hacia el anterior pero de una forma más fácil de leer y escribir agregara alguna que otra palabra reservada para representar la misma idea anterior pero en menos líneas pero en esencia lo mismo.

EL nuevo super cool lenguaje seguirá usando las mismas ideas de siempre pero aquí el detalle en el que "innova" el nuevo lenguaje elegirá quitar las funcionalidad que a su criterio sean perjudiciales (no crean nada) y tratara de agregar todo el azúcar sintáctico necesario para que pueda ser fácil de usar "las buenas practicas" que considere correcta(en realidad nada nuevo solo azúcar) y luego todo los despistados dirán "hoo es algo mega espectacular que nadie hizo antes una primicia"

Lo mismo pasa con los principios. Los principios son más viejos y son los mismos para todos. A veces me río al ver a varios tratando de crear estar a la última de la moda viendo la última herramienta que salvara el mundo y todo el tiempo memorizando una larga lista de wizars de instalación, una larga lista de Acrónimos todos con j(n si están en .net una larga lista de pasos de como jalar de la paleta en un editor WYSIWYG sin entender lo que hacen ni porque lo hacen. Todos esos despistados y que perdieron el rumbo deberían preguntarse si realmente entienden los principios básicos de cómo hacer software de calidad que son exactamente los mismos que fueron y serán siempre iguales para todas las herramientas y no jalar de una paleta a lo vestía creando miles de líneas auto generadas sin entender lo que ellos mismos hacen

En resumen la programación es tan joven que no tiene tiempo de crear nada revolucionario todo es una corrección de lo anterior. Los paradigmas son los mismos inventados hace mucho tiempo. Los principios son los mismos para todas las herramientas y casi todos se preocupan en las modas sin entender realmente los principios para crear software de calidad

marzo 14, 2013 | Unregistered Commenterluis

lucho: totalmente de acuerdo, para hacer estructuras de datos disfrazadas de clases mejor utiliza una estructura de datos directamente, en java lo más parecido a un struct de C de toda la vida es lo que tu has puesto.

jmarranz: yo soy defensor de los modelos ricos ya lo sabes. Pero claro, modelo rico no quiere decir modelo gordo/idiota. No vas a meter todo en la clase "persona", puedes pensar en roles y tener distintos tipos de persona en función del rol que cumplen en tu sistema, entonces tendrás "camarero", "policia" y "jardinero" (ejemplo estupido, pero no tengo ahora más inspiración jeje).

DCI por ejemplo propone diferenciar Data (clases como las que decía lucho) del Contexto y la Interacción. Basicamente la interacción son los mensajes que se pasan entre clases y el contexto es el encargado de añadir a los datos los métodos necesarios en ese contexto. Un ejemplo, suponer que queremos dar de baja a usuarios que se registraron pero no confirmaron su registro en un plazo de tiempo determinado:

class User {
def name
def email
def ....
}

class ExpireCapability {
def expire() {
....
}
}

Luego cuando construyes tu objeto usuario dentro del contexto del proceso de expiración haces:

def user = new User(...)
user.metaClass.mixin = ExpireCapability

y cuando lo vas a usar haces algo como:

user.expire()

Es una técnica interesante, tiene un coste en ejecución evidente con tanta metaprogramación claro esta, pero tienes un modelo rico COHESIVO, que es justo lo contrario de lo que tu criticas con modelos ricos/gordos con clases persona que no tienen ninguna cohesión.Otra opción es simplemente no tener una clase User y constuir objetos tipo ExpirableUser o similares. Es decir, pensar más en los roles y los mensajes que en los tipos de los objetos.

Esto te permite luego hacer cosas como:

users.withConfirmationDateExpired().each { user ->
user.expire()
}

siendo users un repositorio/dao de usuarios. A mi ese código me parece simple y un reflejo directo de la logica a implementar. prefiero esto con mucho a tener un servicio de expiración de usuarios y tener que hacer algo como esto dentro del each:

userExpirationService.expire(user)

Me parece mucho más semantico y más claro que el objeto user sea el que sepa como se tiene que expirar a tener ese conocimiento en un servicio de estos sin estado (es decir, un procedimiento de toda la vida pero disfrazado de clase). Además, en el caso de DCI, podría añadir la capacidad de expiración a cualquier otro "data" que pudiera expirarse. En este caso no parece muy útil, pero pensar en una web donde se pueden poner comentarios a distintos elementos y poder añadir algo como CommentCapability a cualquier objeto.

Es un ejemplo tonto (aunque ese each de arriba esta tal cual en código que tenemos ahora en producción y es todo lo que encontrarás si miras dentro de la clase que representa nuestro proceso de expiración, ¿no es sencillo?), la diferencia puede parecer pequeña, el problema es cuando las cosas crecen, tener un modelo rico permite identificar mucho más claramente tu logica de negocio con tu código, permite limitar el acoplamiento, la tontuna de los getters&setters y sobre todo: limitar la complejidad y hacer código facil de entender, que no hay nada más importante en cualquier sistema software que quieras que sobreviva dos inviernos.

El código es groovy claro, que se ejecuta en todas vuestras JVM's jeje. (y ahora tiene tipado para los que sean más cobardes :P )

marzo 14, 2013 | Registered Commenteralfredocasado

La verdad es que no me va mucho tu enfoque aumentas la cohexión pero también un poco el acoplamiento (de user sale la funcionalidad de expirar que puede ser una acción persistente)...

Digamos que tu user.metaClass.mixin = ExpireCapability es un acoplamiento light en un modelo rico, no es exactamente la visión optimista ochentera tras el "invento" de la POO del todo-junto-en-la-misma-clase.

La parte buena es que al final el código de expiración lo has puesto en ExpireCapability, pero ciertamente no me gusta que los métodos de ExpireCapability acaben fundidos en User, acabas añadiendo a User miriadas de métodos de lo más variopintos, ligados al mismo objeto/clase, al final acabas viendo un user.save() ¿save en donde? ¿a través de que medio? ¿donde está el contexto? etc

Si de user saliera un sencillo getExpireCapability() ahí acabarían mis pegas pues tampoco hay que cogérsela con papel de fumar, sí te confieso que en algún caso algún objeto de "servicio" mio sale de algunas clases de mi modelo de datos pero no lo cuentes mucho por ahí :)

Pero en fin para gustos colores al menos colores razonados :)

marzo 14, 2013 | Registered Commenterjmarranz

Fijate que no añades montones de métodos, precisamente por el contexto. En cada contexto añades sólo los necesarios, por ejemplo en este caso el contexto sería el Proceso de expiración. Es un poquito más complicado, pero simplemente el repositorio recibe una factoría que es la que se encarga de crear añadir los métodos, y es el proceso el que instancia la factoría y por tanto el que decide que clases añadir en este contexto.

algo como:

def users = new UsersRepository(factory: new UserExpirationProcessFactory())

este código estaría dentro de la clase "UserExpirationProcess", o bien en una factoría de procesos. El tema de la persistencia tampoco añade más complejidad, simplemente si el proceso de expiración de usuario requiere algún resultado persistente, que es de esperar que si, pues la capability tendrá el colaborador necesario, la construcción de objetos sería algo como:

def userWriterSQL = new UserWriterSQL()
def userProcessFactory = new UserProcessFactory(userWriter: userWriterSQL)
def userReader = new UserReaderSQL(factory: userProcessFactory())

Luego el factory al crear los usuarios les pasaría el "userWriter", además ese colaborador esta en la capability no en el user. Cuando quieras cambiar la persistencia a otro sistema sólo tienes que cambiar el objeto userWriter que instancias y listo. no veo ningún problema en incluir la persistencia en la ecuación y que la operación de expiración haga algo como:

def expire() {
status = 'expirado'
userWriter.update(this)
}

No he metido conexiones ni transacciones, pero es facil de resolver, simplemente haciendo que los writer/reader reciban un conection o similar, y para transacciones con alguna clase unitOfWork que permita "demarcarlas" usando closures por ejemplo que es más bonito que las anotaciones o AOP.

Todo esto en el libro de copliend Lean software architecture esta mucho mejor explicado y con mejores ejemplos. Su idea va un poco más lejos y es algo así como que cada "contexto" es una historia de usuario o una feature, de modo que estas son autocontenidas y permiten una mejor evolución del sistema en función de los requisitos de usuario (de ahí el titulo de "lean software"). Personalmente sólo he usado ideas sueltas de este enfoque y no en su totalidad porque no tengo claro como encajarlo muchas veces, pero me parece una idea muy interesante de la que se pueden sacar bastantes cosas. Al fin y al cabo se trata de construir sistemas basados en pequeñas features independientes, en el fondo algo de filosofia muy "unix", y otra forma más de luchar contra la complejidad y controlar el crecimiento del software, que es nuestro problema más duro de resolver.

Ademas te gustara saber que copliend es un defensor de cosas como la programacón por contrato frente a TDD que no le gusta mucho jeje. Es más de los tuyos que de los mios, pero hay que leer de todo :P

marzo 14, 2013 | Registered Commenteralfredocasado

Que puede aprender un programador (de lo que sea) de clojure *sin aprender clojure*: nada.
O bueno, cosas que ya deberia saber y que deberia haber aprendido por otras fuentes, ya clasicas. Fuentes clasicas y obviedades que tienen sus raices en en la programacion funcional que inicio su andadura en los 50...pero es es (otra) historia.

En cuanto a la inmutabilidad la *unica* diferencia es que en clojure por defecto los datos son inmutables. Sin embargo te deja usar estructuras mutables con construcciones relativamente sencillas si lo necesitas.
La clave esta en que estas son necesariamente seguras concurrentemente.
Si aun asi necesitas bajar mas al nivel de los threads o locks (o no te tienes que preocupar de ellos) puedes hacerlo, pero no te lo facilita.

Es algo asi a lo que hizo java con la gestion de memoria manual con punteros de c++: caparla. Sin embargo clojure es mas flexible y si que te deja bajar todo lo que necesites.


user> (def array (java.util.ArrayList.))
#'user/array
user> (dotimes [n 1000000] (.add array n))
nil
user> (def counter (atom 0))
#'user/counter
user> (dotimes [i (count array)] (swap! counter + (.get array i)))
nil
user> counter
#<Atom@119f5a1: 499999500000>

Hablamos entonces de facilitar usar objetos inmutables o mutaciones thread-safe y dejar a los usuarios avanzados que se lo salten o en facilitar mutar alegremente los objetos a cualquier usuario como si los threads, la concurrencia y el paralelismo no existieran. Y eso ya es malo ahora pero cada vez es peor.

Por que en el mundo real las cosas suceden simultaneamente...

marzo 14, 2013 | Unregistered Commenterjneira

¿Soy yo el único al que le ha dolido lo de "tiene un coste en ejecución evidente con tanta metaprogramación"? Después llega alguien con un resumen de los registros del SMF o con un pantallazo de un top y al final las cosas se terminan haciendo en Cobol o en C...

marzo 15, 2013 | Registered Commenternilojg

Pues no se donde esta el "dolor", como todo depende, el coste en ejecución de la metaprogramación es "evidente" pero puede ser relevante o no en función del problema que estes resolviendo. Es sólo un factor más a tener en cuenta, pero vamos, si hablamos de rendimiento en casi cualquier aplicación de gestión el cuello de botella esta siempre en el mismo sitio: en el IO.

Además a partir de java7 con invokeDynamic la cosa se minimiza bastante. De todos modos cada cosa para lo suyo, si tienes que hacer algo donde el rendimiento es fundamental y no se trata tanto de IO como de procesamiento de datos en memoria (un decodec de video por ejemplo) pues entonces a lo mejor claro que lo tienes en C, que por cierto, tampoco creo que sea ningún problema.

marzo 15, 2013 | Registered Commenteralfredocasado

Muy interesante el planteamiento de Alfredo Casado, no lo habia visto expuesto de forma sistematica como patron. Interesante y a tener en cuenta como herramienta si estas trabajando con oop dinamica.
Pero tambien es interesante como ejemplo de la maquinaria necesaria cuando estas en ese contexto. En la oop el objeto no sigue el principio de la responsabilidad unica: es un artefacto que mezcla el concepto de modulo/espacio de nombres de funciones con el de estructura de datos y el tipo de datos.
Esta mezcla unida al polimorfismo tiene una serie de ventajas pero mezclar cosas que tal vez debieran estar separadas complica las cosas.
En unix cada programa tiene una responsabilidad. No hay un elemento "superior" que le inyecte comportamientos en funcion del contexto en que se ejecuta usando factorias en las que vive el codigo que ejecuta ese comportamiento (escrito todo seguido chirria mas).
En cada contexto se eligen los programas concretos con responsabilidades concretas y se combinan para conseguir el comportamiento deseado, aprovechando que tienen un interfaz comun (casi todos reciben y devuelven cadenas). El codigo esta separado de los datos (las cadenas)
Este modelo y la programacion funcional que clojure fomenta tinen muchos puntos en comun.

(defn expire (writer anything) (write writer (assoc anything :status :expired))
(defn expire-with-db-writing [db-writer] (partial expire db-writer))
(defn expire-with-file-writing [file-writer] (partial expire file-writer))

http://clojure.org/rationale

marzo 16, 2013 | Registered Commenterjneira

Pregunta para Alfredo

¿de verdad que hay separación de responsabilidades en este código?

def expire() {
status = 'expirado'
userWriter.update(this)
}

Lo digo porque se me ocurren otras muchas perrerías que podemos hacerle a User e imagino a User con 10 atributos gestores, es decir métodos con 10 responsabilidades distintas. Aunque reconozco que si las inyectas circunstancialmente (sólo posible en un leng dinámico) la cosa se alivia un poco.

A ver se puede vivir con ello no es grave pues lo gordo se hace fuera de User, vale está todo más cerquita pero sinceramente ese enfoque está cerca de la crítica barata de la POO que dice que en POO se programa todo en la clase User (en tu caso no es estrictamente cierto vale) como si no hubieran transcurrido 30 años desde que la POO empezara a popularizarse y no nos hubieramos dado cuenta de que la vida es más complicada que el tutorial simpático de introducción a la POO.

Mi defensa de los modelos anémicos es precisamente la renuncia a ese tipo de programación porque las cosas son mucho más complicadas para encajarlas fácilmente en la idea fundacional tan simpática de la POO de encapsulación de atributos y métodos asociados, digamos que mi visión de la encapsulación va más allá de la clase, por ejemplo sólo UserDAO persiste y carga User lo cual es una especie de encapsulación multiclase del mundo de User.

Respecto a las reflexiones de jneira...

Vamos a ver, estamos en 2013 no a principios de 1980 cuando Bjarne Stroustrup estaba ingeniando C++ como una extensión de C (de hecho el primer compilador de C++ no era tal sino un generador de código C), es decir me da una pereza infinita hacerme una lobotomia e ignorar 30 años de OOP, la OOP nace porque la realidad del mundo del desarrollo software es MUCHO MÁS COMPLICADO QUE EL MUNDO PURO DE LAS FUNCIONES DE VARIABLES REALES en el que muchos matemáticos viven agusto.

Yo no he disfrutado ni de coña de tantos años de POO, no soy tan mayor, pero joder algo tendrán de útiles cuando ves código que mueve esas cosas tan supermodernas que usamos ahora y ves detrás los mismos patrones de programación inventados hace décadas y se te saltan las lágrimas porque hoy día en singletonlandia te cuesta mucho verlas.

Por eso digo que me aburre meterme ahora a una disertación teórica de si venimos del mono o de una costilla, pues no, el 99% de las evidencias apuntas a que venimos de los monos por lo que los defensores del 1% tendrán que hacer algo más que "añadir dudas al 99%" sino conseguir que el 1% de evidencias de la costilla sea un 20%, un 30%, un 60%, hablemos de evidencias, de hechos reales y probados que vayan dando consistencia a la teoría de la costilla en la resolución del problema de la evolución humana suponiendo que sea incompatible con la otra teoría. Es decir menos disertaciones sobre la "superioridad" de la prog funcional (que parece que es incompatible con la POO aunque yo no lo vea) y más software funcional real, menos fibonacci y más aplicaciones CRUD y más aplicaciones y TOOLKITS intensivos en UI.

Y lo siento pero Scala no me vale como ejemplo de esto, Scala es una vuelta de tuerca más a la POO, por ejemplo sus traits apestan a herencia múltiple, su creación de objetos add-hoc no es mucho más que declaración de clases anónimas con una única instancia. Sueño con algunas funcionalidades OO de Scala en Java y si es necesario mandaré a paseo a Java el día que Scala cale de verdad y Java decaiga y si Android se programara en Scala no lo dudaría ni un minuto.

Aprecio la programación funcional pero a día de hoy no la veo más que en un nivel pura microprogramación algoritmica con un gran enfasis en la reutilización de los algoritmos en funciones lo cual se agradece y mucho y a lo cual me apunto, lo demás, la gurulandia, es cabreante.

Cuando para empujar algunas ideas implica "combatir" otras ahí la cosa se tuerce porque yo soy más de SUMAR.

Como decía en 2013 si la programación funcional quiere "batir" a la POO tiene que hacer algo más que disertaciones sobre si un objeto no sigue el principio de responsabilidad única.

Y como estamos en 2013 un poco de código del mundo real:

API C del codificador de MP3 LAME portado a Java:

http://openinnowhere.sourceforge.net/lameonj/javadoc/lame/std/Lame.html

¿Veis como yo decenas de veces el parámetro lame_global_flags?


Encapsulación de los métodos fundamentales de la API de LAME en una API orientada a objetos:

http://openinnowhere.sourceforge.net/lameonj/javadoc/lameonj/encoder/std/GenericEncoder.html

Interface base;

http://openinnowhere.sourceforge.net/lameonj/javadoc/lameonj/encoder/std/Encoder.html

La implementación por defecto es capaz por ejemplo de analizar si el orden de las llamadas es correcto, ¿por qué? porque guarda internamente (encapsula) el ESTADO de lo que estamos haciendo:

protected boolean closed = false;
protected boolean encodingTaskInProcess = false;

Y a través de herencia aprovechar la "sabiduría" de encoding genérica para ofrecer un encoding más avanzado a través de streams:

http://openinnowhere.sourceforge.net/lameonj/javadoc/lameonj/encoder/std/StreamEncoder.html

A su vez necesitando más estado para conseguir su propósito:

protected BufferedInputStream sourceStream;
protected byte[] pPCMBuffer;

E internamente por herencia usando una clase StreamEncoderPCMImpl o StreamEncoderWAVImpl según si el fuente es un WAV o no de forma totalmente transparente al usuario de la API, circunstancia que la clase base StreamEncoderImpl no necesita conocer y que su comportamiento específico que resuelve por polimorfismo.

Sinceramente yo prefiero la API orientada a objetos, más segura, más fácil de aprender y para el programador de la misma le ofrece la máxima reutilización de código (y eso que si la volviera hacer la haría de un sólo encoding no reutilizable y quizás con algún método fluido, es decir sería un poco más funcional).

Hay montones de APIs C de cientos de métodos que se aplican a unas cuantas estructuras, todas ellas muy funcionales hasta con punteros a función (las famosas callbacks tan funcionales que añadir un dato de estado era imposible). Una de las cosas que fascinaron brutalmente de Java fue su API orientada a objetos, coherente, compacta, robusta, extensible, frente a la jungla de la API C o incluso de la antigua API de PHP.

¿Queremos volver a eso aunque tenga un barniz de modernidad?

Yo sueño en mi pobre Java con traits que no es más que una herencia múltiple capada, sueño con closures pues hacer inner classes o declarar clases muy add-hoc es un coñazo, sueño con una AOP basada en anotaciones y en tiempo de compilación que sea sencilla que me permita extensibilidad horizontal, sueño con poder congelar objetos inicialmente mutables (que se podría hacer con AOP), sueño con la sobrecarga de operadores, sueño con generics que no se pierdan en compilación o que se resuelvan en tiempo de compilación para cada tipo concreto como en C++, sueño con poder declarar métodos abstractos implementados en tiempo de ejecución en el objeto concreto, sueño con parámetros por referencia y atributos-objeto-por-valor, sueño con poder ¡¡SUMAR!!

marzo 17, 2013 | Registered Commenterjmarranz

Claro que hay separación de responsabilidades, ese método sólo se ocupa de lo necesario para expirar un usuario, si tiene dos lineas el método!!. El método es simple y se entiende de forma inmediata y esta abstraído de la implementación concreta de persistencia usando una abstracción.

cual es la alternativa en un módelo anemico?, algo como esto:

UserExpirationService {
def userDAO

def expire(user) {
user.setStatus('expired')
userDAO.save(user)
}
}

Luego desde donde user este necesitaré hacer algo como:

def expirationUserService = ... (alguien lo inyectará o lo crea o lo que sea)
def user = userDAO.get(...)
expirationService.expire(user)

en lugar de hacer algo como:

userRepository.get(...).expire()

En un ejemplo tan pequeño no parecen tan diferentes uno de otro, sin embargo no entiendo porque necesito un objeto que tenga que hacer lo que debería hacer el usuario. En realidad esto es hacer de user una estructura de datos y el servicio es un procedimiento, vamos que este modelo anemico es ni más menos que volver a la programación estructurada (estructuras de datos globales manipuladas a través de funciones sin estado). Espero que los 30 años de OO hayan servido para algo más que para darse cuenta que lo mejor era el paradigma estructurado. El módelo anemico es ir en contra de la encapsulación y la abstracción, que son (muy por encima de cosas como herencia o incluso el polimorfismo) lo que define lo que es la OO.

Si tu piensas que es un usuario en el mundo real te salen tropecientas responsabilidades de ese usuario, precisamente para evitar esto, que daría lugar a objetos monstruosos y poco/nada cohesivos es posible pensar en roles en lugar de pensar en "cosas del mundo real".

Y este ejemplo, a la DCI, era una posibilidad, no es la que uso habitualmente aunque si lo he usado en ocasiones. Lo más obvio en un lenguaje como java que no tiene estas capacidades dinámicas es simplemente crear objetos que no representan la entidad en el mundo real sino que representan un rol en ese mundo, por ejemplo en lugar de un user tendrías un "ExpirableUser" o simplemente un "Expirable", y en lugar de un repositio/DAO de usuario tendrías uno que te devuelva cosas que se puedan expirar. Esto ultimo claro esta es difícil de hacer si pensamos en un mundo donde la relación entre persistencia-objetos es 1-1.

Mi critica al módelo anemico tiene mucho que ver con el mundo "enterprise" donde con un ORM en una mano y un motor de DI en la otra la gente esta haciendo programación estructurada de la de toda vida y muchos de ellos pensando que están haciendo la "arquitectura enterprise" más avanzada que existe... Cada vez que veo un legacy de java (de estos de spring/hibernate/jpa/jsf y todas estas cosas) me reafirmo más en esto ultimo. Espero que con modelo anemico no te refieras a esto, porque a mi me parece un paso atras terrible.

jneira: Sobre la posible solución funcional no puedo decir mucho, no tengo experiencia suficiente en este paradigma para poder hacer un análisis mejor. Aunque es algo que estoy solucionando, prometido jeje

marzo 18, 2013 | Registered Commenteralfredocasado

Al final Alfredo la diferencia no es tan grande :)

expirationService.expire(userDAO,(Expirable)userDAO.get(...))

Siendo:

ExpirationService {

def expire(dao, expirable) {
expirable.setStatus('expired')
dao.save(expirable)
}
}

personalmente no me entusiasma que esté la funcionalidad en User pero poco, poco más, mientras haya distribución de responsabilidades "en lo gordo" me vale :)

Si al final tienes un Writer como lo llamabas más arriba (=DAO) y tienes un método expirable() que es prácticamente idéntico a ExpirationService, yo lo más probable es que ni siquiera definiera tal ExpirationService si la expiración de un user implicara siempre salvar y no hubiera más tipos objetos "expirables".

Respecto a que yo represente el mundo "enterprise" como modo de hacer las cosas... buff teniendo en cuenta de que durante años he sido uno de los poquitos gilipollas del mundo (gilipollas porque te ganas gratuitamente el odio de los talibanes de la tecnología X de turno) que ha criticado el mundo Spring como un motor viral de declaración de singletons y por tanto el gran creador de la singletonlandia actual que como bien dices es poco más que programación estructurada un poco más moderna (quizás es más bien un cierto abuso de Spring pero un abuso considerado como "buena práctica" por muchísimos). También he criticado un día si y otro también el uso de XML como lenguaje de programación capado típico de toolkits MVC (sin duda prefiero el hardcode a la automutilación), la introducción de código Java/scripting de acceso al modelo de datos en las vistas típico de JSF y similares etc etc.

Pero sí, en lo de los modelos anémicos reconozco que sí soy "ortodoxo" :)

Yo utilizo a saco la herencia de implementación por lo que se que en ese tema no estarás de acuerdo :) y es esa una muy muy importante razón por la que no creo en los modelos ricos. No no es porque necesite meter herencia en los modelos (no digo que algún caso muy obvio y muy claro lo sugiera) y no pueda, es precisamente lo contrario, eran herencias forzadas lo que me invitaban los modelos ricos, acababa definiendo un UserExpirable y un UserNoExplirable porque encajaban muy bien en funcionalidad ViewUserExpirable ViewUserNoExpirable metidas en las propias clases User, es decir herencia perfectamente útil en la View acababas propagándola a la propia clase User por CONVENIENCIA de una fe en la encapsulación excesivamente dogmática, herencia cuyo valor en la clase User estaba MUY por los pelos (teniendo en cuenta la existencia de otros aspectos ortogonales en User diferentes a la expiración).

El problema mayor surgía cuando un simple estado expirado implicaba dos tipos de visualización muy diferentes que por tanto era imposible modelar como UserExpirado UserNoExpirado porque el mismo objeto User puede o no estar expirado, debido a que User no tiene herencia en este caso acababas sacrificando utilísimos ViewUserExpirado y ViewUserNoExpirado por un ViewUser en donde a base de ifs guarreros elegías las dos visualizaciones que eran muy diferentes y todo porque la View está pre-creada y metida en el User lista para ser usada.

Otro problema era debido a que otro aspecto, por ejemplo la persistencia admitía una interesante herencia respecto a un aspecto y no podías aplicarlo en User porque ya habías elegido Expirable y NoExpirable o acababas haciendo mierdas tipo UserExpirableCorporative, UserExpirableIndividual, UserNoExpirableCorporative y UserNoExpirableIndividual y ese tipo de cosas que hace quien abusa de la herencia (aunque hoy día es raro que encuentres a alguien abusando de la herencia sencillamente porque lo raro es que simplemente esté usando la herencia y no por principios sino por ignorancia).

Conclusión: a la puta calle la View y todo servicio fuera de User, los principios están bien hasta que se convierten en un problema.

Te puedo poner un ejemplo real cercano, la serialización de un árbol DOM como sentencias de JavaScript válido para un cierto navegador.

No puedes imaginar la basura que eran ciertos navegadores, por ejemplo el Pocket IE de Windows Mobile, el cual admitía AJAX y se podía hacer hasta Single Page Interface (sí sí lo vieron mis ojos), llegué a hacer una herencia como ésta: :

JSRenderImpl
JSRenderNodeImpl
JSRenderElementImpl
JSRenderHTMLElementImpl
JSRenderHTMLElementMSIEOldImpl
JSRenderHTMLElementMSIEPocketImpl

¿inyecto ese objeto JSRenderHTMLElementMSIEPocketImpl en todos los DOM Element HTML? bueno realmente en todos los nodos DOM pues todo nodo DOM tiene un serializador asociado.

Como se puede ver en este ejemplo el objeto usado JSRenderHTMLElementMSIEPocketImpl no sólo depende del tipo de datos del objeto protagonista que es un org.w3c.html.HTMLElement estándar Java sino también de un contexto de uso que es "serializar para el navegador Pocket IE".

Ese árbol usa herencia, polimorfismo, encapsulación (dentro hay atributos con listas de tags que admiten innerHTML etc), no es precisamente programación estructurada, el código llamadador apenas ejecuta un método factoría pasando el nodo y el navegador y obtiene un JSRenderNodeImpl.

¿Qué pasa si necesito que el árbol DOM sea compartido entre varios navegadores (caso de la funcionalidad de control remoto)?

¿Cual es la alternativa? ¿Herencia = mala y meto todo en un JSRenderNodeImpl lo inyecto en todos los nodos DOM y hago una fiesta de ifs dentro de JSRenderNodeImpl según tipos de nodos y navegadores? ¿en el mejor de los casos con unas decenas de composiciones anidadas para soportar los múltiples tipos de nodos y navegadores debido a que me he auto-impuesto un modelo rico con servicios pre-inyectados y eliminada la herencia porque es "mala"?

Los dogmas, los principios (la encapsulación, la cohesión) pueden ser buenos pero cuando se convierten en absolutos bienvenida la herejía :)

marzo 18, 2013 | Registered Commenterjmarranz

Las vistas y la persistencia, los "bordes" del sistema, son donde más que habitualmente te tienes que saltar la encapsulación. Tanto la vista como la persistencia necesitan tener acceso a las propiedades del objeto para guardarlas o pintarlas, y esta claro que el user no puede pintarse/guardarse directamente porque es una responsabilidad que desde luego no corresponde al "core" de tu modelo.

En estos casos no veo problema en usar estructuras de datos en lugar de objetos (me niego a decir DTO's, no necesito un nombre de patron para justificar una decisión jeje). De nuevo usando lenguajes dinámicos esto se simplifica enormemente, ejemplo:

class User/ExpirableUser/... {
def dataForView() {
[fullName: name + surname,
registerdate: registerdate
status: ....]
}
}

Estoy devolviendo un mapa con datos, que es lo que quiero visualizar: datos. El lenguaje en este caso me facilita tanto la creación del mapa como su acceso después (que es simplemente obj.propiedad, azucar sintáctico mu rico jeje).

En este tema existe otra idea interante que es CQRS, basicamente va de separar la vista de las acciones. Digamos que tu modelo rico es para las acciones mientras que para las vistas no construyes un modelo rico, haces querys y pintas datos. Luego hay un rollo de arquitectura asincrona y orientada a eventos, pero sin entrar en esta parte, la separación vista/acciones es interesante y simplifica la vida pensar sólo en querys y datos cuando piensas en la vista y pensar en un modelo rico cuando piensas en acciones. No es aplicable a todos los casos, pero en la tipica aplicación de gestión web funciona bastante bien.

Sobre el tema de la herencia, sin conocer ese problema más en detalle pues no se cual sería la alternativa, la maraña de if's desde luego que no, mejor herencia que eso claro esta. Con composición quizá se podría crear un Render agregando las capacidades necesarias como colaboradores. En realidad es lo mismo que lo que estas haciendo, pero en lugar de construir tu render en tiempo de compilación con la herencia lo construyes en tiempo de ejecución con composición.

Ya te digo que no entiendo el problema en profundidad, pero en general no me he encontrado muchos casos donde no se pueda cambiar herencia por composición, es una cuestión quizá más de planteamiento inicial, porque llegado un punto ya no es solo preguntarse "se puede cambiar?" también hay que preguntarse "merece la pena?". Si funciona y es mantenible, hay muchos caminos para llegar a una solución, eso es lo divertido de esto jeje.

marzo 18, 2013 | Registered Commenteralfredocasado

"no me he encontrado muchos casos donde no se pueda cambiar herencia por composición"

Y no encontrarás ninguno, elegancia, reutilización etc no tienen nada que ver con "imposibilidad", todo acaba en ifs y gotos, en ands y ors, en unos y ceros.

marzo 18, 2013 | Registered Commenterjmarranz

Al final lo que iba a ser un comentario se me fue un poco de las manos, asi que he publicado un post con el codigo to bonito
http://javierneirasanchez.blogspot.com.es/2013/03/sobre-el-polimorfismo-en-clojure-y-la.html

marzo 19, 2013 | Registered Commenterjneira

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>