Arquitectura correcta/Patrón de diseño

Buenas tardes compañeros de javamexico, tengo la siguiente duda y acudo a ustedes para poder resolverla:

Me encuentro desarrollando un proyecto donde estoy modelando distintos tipos de casas, cada una de ellas cuenta con distintas propiedades y métodos. En este proyecto cada usuario es capaz de exportar estos tipos de casas a distintos formatos (PDF,EXCEL,CSV,etc):

Mi duda es sobre la forma en la que tengo organizada mi arquitectura (quisiera saber qué opinan de ello y si hay una mejor forma de hacerlo). La forma en la que yo resuelvo esto es la siguiente: Tengo una clase abstracta llamada Casa y en ella tengo métodos abstractos que serán sobreescritos por cada tipo de casa para indicar cómo se pintarán en un archivo excel,pdf, csv, etc.

 

Me surge la duda si esto es lo correcto o si existe algún patrón de diseño para resolverlo, deseo tener una mejor arquitectura en este proyecto y al mismo tiempo seguir aprendiendo... Será que necesito algún patrón de diseño de tipo factory? o como lo estoy haciendo es correcto?
De antemano, muchas gracias por su tiempo y conocimiento aportados

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de The man

A mi me parece

Yo lo dejaria asi,con un clase abstracta,lo que si agregaria es una clase por cada formato,Ejemplo la clase excel que tenga todos los metodos para exportar a excel para en el metodo llamar a la respectiva clase de cadar formato y exportar,esto por cuestiones de reutilizar codigo

Imagen de ezamudio

completamente distinto

Yo no le pondría nada de eso de formatear a la casa, y por lo tanto no necesita ser abstracta. En vez de eso tendría una interfaz o clase abstracta   y luego implementaciones concretas  ,  , etc.

En la interfaz defines  .

De ese modo cuando quieres exportar a un formato, obtienes el exportador correspondiente. La casa no tiene por qué saber si la van a exportar a un formato, eso es cosa del exportador.

instanceof?

ezamudio:

A eso se refieren cuándo hablan de pojos ? En bean sólo debe conocer sus propiedades, getters y setters? Y la lógica de todo lo que se hará en ese pojo va por aparte? Si es así, cuáles son las ventajas de hacerlo de esa manera ?

Siguiendo lo que me aconseja, quedaría algo así:

 

El problema que a mi me surge es que para exportar cada tipo de casa, necesitaría operadores instanceof, ejemplo:

 

Mi pregunta es, cómo puedo evitar utilizar operadores instanceof en un exportador concreto para todos los tipos de casa? El problema está en que cuando agrego una nueva casa, podría olvidar poner la nueva casa en el if y eso me llevaría a no tener por ejemplo el exportador pdf de esa casa.... me gustaría que cuando agregue la nueva casa el código me fuera pidiendo qué métodos debo implementar (esa era la única ventaja de tener una clase abstracta, que cuando extendía de ella, ya sabía qué era todo lo que tenía que implementar y ya no tenía que estar escribiendo sentencias if para cada exportador concreto)

Gracias de antemano por su tiempo, gracias también por tus comentarios #The man

Ventajas de los patrones de diseño

Pues una de las principales ventajas de un patron de diseño es que genera bajo acoplamiento. Como bien dice ezamudio, es mejor que uses interfaces a una implementacion, eso se le conoce como composicion.

Supon que tienes una interface y esa interface define tu generalmente lo que hace:  ... ¿Que exporta? un arreglo de bytes por ejemplo. ¿En que formato? eso si aun no lo sabemos

 

ahora tenemos la clase que implementa:

 

Oooops pero ahora tenemos un  . Chispas, es hora de generar otra interface porque podemos exportar a diferentes tipos, CSV, Excel, etc

Ahora lo que vamos a hacer son los comportamientos.

 

y finalmente la implementacion que hará la talacha de exportar a digamos CSV

 

Ahora que tenemos nuestra arquitectura ahora si a utilizar (por cierto, le faltan los setter y getters). Mi implementacion para el CSV seria

 

Ese patron segun recuerdo es Startegy, para desacoplar

Imagen de ezamudio

abstracciones

Tal vez estoy viendo el ejemplo de manera muy simplista, tal vez escogiste una mala analogía... si lo de las casas es real, pues creo que la abstracción que estás haciendo no es la apropiada. CasaCarton no debería ser una subclase de Casa, sino que Casa debería tener una propiedad que indique el material del cual está hecha. Lo mismo va para CasaPlaya; hay ciertos atributos que dicha casa va a tener, cuyos valores serán muy distintos de una casa de ciudad, pero esto que ilustras es como decir que CasaAmarilla y CasaRoja son dos subclases distintas de Casa, cuando en realidad deben ser instancias de una Casa y simplemente cuando les pides su color una te da amarillo y otra te da rojo.

Suponiendo que por alguna otra razón más compleja necesitas realmente que Casa sea abstracta y vas a tener subclases (y esa razón no tiene nada que ver con lo de exportar), entonces otra opción es que Casa tenga UN método abstracto   que recibe un Exportador como parámetro. En este caso la interfaz Exportador es bastante más complicada y puede ser digamos así (ejemplo sobresimplificado):

 

Entonces, cuando creas tu exportador a Excel debes implementar todos esos métodos, igual cuando haces tu exportador a CSV o a PDF etc. Si luego resulta que le agregas algo a la clase Casa, como por ejemplo Garage, entonces le agregas a la interfaz Exportador un método   y el compilador te recordará que debes implementarlo en todas las implementaciones concretas de Exportador.

Del lado de la Casa, puedes incluso implementar el método   y pasarle a dicho exportador todo lo que sepas de la Casa abstracta; las subclases concretas pueden sobreescribir dicho método, llamando la implementación de la clase abstracta (con  ). Habrá subclases que ni siquiera necesiten sobreescribir el método y otras tal vez quieran hacer algo distinto y por eso lo sobreescriben y tal vez llaman a super o ni siquiera.

Imagen de bferro

Casa, expórtate! o exportador,exporta la casa!

That is the question y al decidirlo, se decidirá si las casas deben tener un método para exportarse, con ayuda de un exportador o el exportador tendrá métodos para exportar la casa que le ordenan. ¿Cuál solución es mejor? Habria que añadir más cosas que aparecerán en ese contexto para dar la respuesta.
Una aclaración pertinente a algo que comentó java.daba.doo: Programar contra interfaces NO es composición, aunque para usar correctamente la composición programamos contra interfaces. Son dos principios de diseño:
Programming to an interface, not an implementation
Favor composition over inheritance.
El artículo Design Principles from Design Patterns donde se entrevista a Gamma vale la pena.

Gracias por sus comentarios

Gracias por sus comentarios ezamudio y java.daba.doo

ezamudio:

Si, seguramente escogí una mala analogía pues no es un ejemplo real y la intención de explicarlo así no era entrar en detalles de lógica sino hacerlo simple. Pongamos otro ejemplo:

SerVivo (clase base), implementaciones (Perico, Dinosaurio, Hormiga, ...)

Entiendo sus comentarios y los de java.daba.doo, pero sigo teniendo la misma duda (la cual no he sabido explicar):

Tomando este ejemplo de los seres vivos: Supongamos que deseo exportar los detalles de la hormiga, el perico y el dinosaurio (tal vez su historia de cada uno) a pdf,excel y csv, también necesito indicar cómo se va a "clonar" cada uno, también necesito indicar la manera en la que cada uno comerá cierto alimento, y N comportamientos más... Pongo en código mi idea para ser más claro:

 

Tengo las siguientes dudas sobre mi arquitectura:

De acuerdo al código anterior, es correcto tener todos los comportamientos que tendrán los seres vivos en la clase abstracta SerVivo ? No hay algún patrón de diseño donde le diga algo así como FabricaDeComportamientos() ?

Mi segunda pregunta es: Es correcto mezclar esto así? He leido que los pojos deben tener solo las propiedades, setters/getters y de acuerdo a lo que les estoy explicando, yo estoy revolviendo propiedades con comportamientos abstractos (en las clases base regresaré una instancia que realice el comportamiento, por eso les llamo abstractos). Siento que estoy revolviendo propiedades con comportamientos, mi pregunta es si la práctica que estoy utilizando es correcta.

Muchas gracias por su tiempo

Imagen de bferro

¿Es correcto tener todos los comportamientos en la clase base?

Tu pregunta, hobo_75 de si es correcto tener todos los comportamientos en la clase base abstracta es una pregunta frecuente, y la única respuesta correcta es: It depends.
¿De qué depende?, de muchas cosas, inclusive del lenguaje en que vas a implementar la solución, sobre todo en el tipado dinámico o estático.
El asunto tiene que ver con la decisión de utilizar la herencia exclusivamente para especialización, o también para extender el comportamiento expresado en la clase base por las clases derivadas. Un ejemplo muy adecuado para discutir esto es la clase java.io.InputStream, que es una clase abstracta con implementaciones por default para algunos de sus comportamientos.
Puedes echarle un ojo al código fuente y a la documentación de esa clase para discutir sobre las decisiones de diseño que tuvieron en cuenta para decidir una herencia por especialización, o una herencia por extensión, o una mezcla de ellas que es lo que sucede realmente.
Cuando haces uso de la herencia por especialización, incluyes en la clase base todos los comportamientos posibles que tendrán las posibles clase derivadas. Es un gran dilema porque es imposible decidir a priori cuando diseñas un API, las posibles extensiones a ese API mediante la herencia.
Lo que haces es una operación de Unión de todos los comportamientos de las clases derivadas y los incluyes en la clase base, y tendrás entonces que decidir cuales de esos comportamientos tendrán que ser abstractos, y cuales necesitan una implementación por default, para evitar que aquellas clases que no tienen ese comportamiento tengan la necesidad de anularlos (overriding).
Al usar ese tipo de solución te salvas de los castings a las referencias que usas del tipo de la clase base. Por ejemplo, si los que diseñaron la clase InputStream hubieran decidido escribir una implementación por default para el método getFD, tendrían que haber dado una implementación por default, indicando el disparo de una excepción, para que así las clases derivadas de InputStream que no tienen asociado el concepto de un file descriptor no tengan que escribir una implementación, y que la clase FileInputStream sí lo haga pues ella si maneja un file descriptor.
Al hacerlo de esa manera la programación con interfaces se facilita y podemos entonces escribir algo así:
 

Al no incluir ese método, que es como está realmente, entonces tienes que escribir:
 

¿Cuál es la mejor opción: herencia por especialización o por extensión? Ambas son útiles y necesarias y su uso depende fuertemente del modelo de objetos del dominio del problema y depende mucho menos de si tienes que usar castings o no.

Re: Casa, expórtate! o exportador,exporta la casa!

Gracias Sr. BFerro
Exacto, ahí se encuentra la respuesta: "Casa, expórtate! o exportador,exporta la casa!" Gracias por su punto de vista en el primer post.

Sobre éste último comentario, yo siempre evito utilizar CAST pero con el ejemplo que me pone del InputStream se me aclara el panorama porque me doy cuenta que tiene más sentido hacer el cast en getFD, pues solo el FileInputStream tiene un descriptor de archivo y no tendría sentido pedir getFD a otro tipo de InputStream.
Con esta gran explicación ahora entiendo cómo hacer mejor mis API's, le agradezco mucho, sin embargo la duda que me queda es la siguiente:

En muchos artículos he leido que la práctica correcta es programar pojos, osea beans planos con sus propiedades, getters/setters. Yo pienso que si introduzco métodos abstractos y la especialización de ellos en clases hijas, estoy rompiendo con aquello de que sean "planos" !
No se si estoy en lo correcto o si exista alguna otra manera de hacer las cosas, por ejemplo yo he visto que en proyectos que utilizan hibernate, sólo se tienen las propiedades anotadas hacia las tablas de la bd a la que mapean, pero no contienen métodos donde definan comportamiento.
Hablando del ejemplo anterior, existe alguna práctica que indique que los objetos de negocio deben ser beans "planos" y para exportarlos se deben crear "exportadores" externos ?

Gracias de antemano a todos por sus valiosos comentarios y aportaciones

Imagen de bferro

¿Qué es un POJO?

Tienes una idea errónea del concepto de POJO. Al rato comento sobre eso, pero por lo pronto un POJO puede tener todos los métodos de negocio que sean necesarios, puede ser abstracto, concreto, o lo que prefieras. En esencia un POJO es un objeto regular de Java, "lo más alejado posible" del marco de trabajo, del contenedor que lo hospeda, etc., etc.,

Re: ¿Qué es un POJO?

Gracias por su gran ayuda, espero el comentario para profundizar

Imagen de ezamudio

POJO

Plain Old Java Object - un objeto de Java que no tiene dependencias con un framework en particular. Por ejemplo no tiene que heredar de alguna clase de EJB o implementar alguna interfaz particular de un framework o llenarlo de anotaciones etc.

Imagen de beto.bateria

Aunque no se habla mucho de

Aunque no se habla mucho de los requerimientos, y con lo que mencionas, utilizaria el patron strategy, y asi quedaria:

 

Le puse que regresa la clase OutputStream debido a que vas a manejar archivos, pero tal ves estoy equivocado, podria regresar un Object. Agregando, no puedes hacer una clase de casa de carton, u otra de madera, eso se debe de especificar en uno de los atributos (material), tal vez si debas hacer una clase edificio, bodega, o departamento.

Considera el patron strategy, el lenguaje java lo utiliza mucho, y yo tambien, es muy... ¿adaptable?, ¿sencillo?, no tengo la palabra exacta, pero ayuda mucho.