Hola! Realicé un diagrama de secuencia y un boceto de modelo para el ejercicio de la libreria puesto en clase. Es un diseño propio, y me gustaría saber si la manera en la que hice las relaciones UML es correcta. Dejo la imagen abajo...
Hola Rodrigo:
Te dejo a continuación mis observaciones acerca de tus diagramas. Tené en cuenta que algunas de las cosas que te señalo no indican que el modelo esté necesariamente mal, sino que los diagramas no son lo suficientemente explícitos o no son coherentes entre sí. Muchas veces sucede que el modelo que uno tiene en la cabeza está dentro de todo bien pero no queda bien plasmado en los diagramas y, por consiguiente, no logra comunicarse efectivamente.
- El diagrama de clases muestra muy poca información. Recordá que oficialmente cada caja de clase tiene tres partes (separadas por líneas horizontales): la primera para el nombre, la segunda para los atributos y la tercera para los métodos. No es necesario mostrar absolutamente todos los métodos y atributos pero tampoco es ideal no mostrar nada más que el nombre. En particular son interesantes los métodos que aparecen en tus diagramas de secuencia y/o aquellos que muestren la responsabilidad de la clase y la razón de existencia en tu modelo.
- Asumo que tu clase Descuento es abstracta, es decir, que nunca la intanciarías y que es solamente una genericidad de los tipos de descuentos que modelaste. Ahí deberías mostrar de alguna manera por qué fue necearia la relación de herencia. ¿Hay responsabilidades compartidas? ¿Cuáles?
- Falta graficar la relación entre Librería y Cliente.
- Faltaría detallar si algunas de las relaciones son de agregación o de composición (con los rombos adecuados).
- Falta modelar la cuenta corriente que se pide en la consigna. En tu modelo solamente es posible calcular el precio de una venta en particular pero no de las ventas de un determinado mes de un determinado cliente como se pide.
- No se entiende qué función cumple la clase Cliente en tu modelo y por qué se relaciona con la clase Venta de esa manera. ¿Una instancia de Venta conoce a muchas instancias de Cliente? ¿Por qué?
- Falta un título en el diagrama de secuencia para saber qué secuencia en particular estás mostrando.
- No llego a entender la caligrafía en los argumentos de los métodos del diagrama de secuencia. ¿Dice "mes"? ¿Eso de qué te sirve? ¿Tenés algún diccionario de meses en algún lado o cómo es que "buscarías" eso? ¿Por qué ese parámetro se va pasando hasta llegar al descuento? ¿De qué le sirve al descuento? En ninguno de los diagramas queda evidenciado eso.
- ¿Por qué los nombres de la mayoría de los métodos empiezan con "buscar"? Si su responsabilidad es la de buscar, i.e., consultarle un atributo a otra clase suena a violación del encapsulamiento.
- No entiendo el método "Anual?(suscripción)" que la Suscripción le envía a Descuento. ¿Por qué envía una instancia de la suscripción como parámetro?
- En el diagrama de clases aparece la clase "Suscripcion" pero en el de secuencia aparece "Suscripciones". ¿Cuál es el nombre que vale en tu modelo?
- Las cajas de iteraciones deberían tener una leyenda que las describan.
Saludos,
Tomás
Hola! Gracias por la respuesta!
- Con respecto al diagrama de clases, es sólo un boceto que estaría indicando las relaciones que hay entre las clases muy por arriba, porque no estaba seguro si era necesario que ambos modelos tuvieran exactamente los mismos nombres. Ahora sé que sí.
- Descuento sería una clase abstracta, ya que hay dos tipos de descuentos, los que son regulares para todos los productos, y los que son anuales que son sólo para las suscripciones. Como se calculan de manera distinta, los puse como clases separadas, las cuales ambas son un descuento.
- ¿La relación entre estas dos seria que el cliente debe ser capaz de poder comprar cosas en la librería?
- ¿Deberia haber hecho una clase Ventas, la cual contenga muchas intancias de Venta, conectándolas con un rombo sin relleno, por ejemplo?
- La consigna dice en una parte 'El kiosco necesita como funcionalidad saber para un determinado mes cuánto debe cobrarle a cada cliente, y lo mismo para un año entero', yo lo que entendi que para un determinado mes, debía devolver cuánto hacía falta cobrarle a cada cliente. En la imagen por ahi no quedo claro, pero arriba del mensaje puse que devolvía totales y clientes, que es el total para cada cliente en un determinado mes. La culpa es mía por no haber puesto el nombre del diagrama, y qué accion realiza.
- En mi modelo la clase cliente tiene los productos que compró en un determinado mes, lo que pienso que debería haber hecho es otro nivel de abstraccion tal que el cliente no sea el que lleve la lógica de los productos que compró en un determinado mes.
- El parámetro 'mes' se pasa porque tomé en cuenta que el precio de los productos puede variar, entonces pregunta el precio y el descuento existente en un determinado mes.
- Ahi rompí el 'Tell, don't ask', voy a corregirlo enseguida.
- 'Anual(suscripcion) pregunta si la suscripción es anual. De ser así, aplica un descuento del 20% sobre la suscripción.
- Debería haber una agregación de instancias de Suscripcion unidas a Suscripciones, me parece.
- Voy a arreglar las cajas de iteraciones.
Descuento sería una clase abstracta, ya que hay dos tipos de descuentos, los que son regulares para todos los productos, y los que son anuales que son sólo para las suscripciones. Como se calculan de manera distinta, los puse como clases separadas, las cuales ambas son un descuento.OK, entonces eso tiene que quedar explícito en el diagrama con la notación adecuada (no debe hacer falta una aclaración aparte para comunicarlo).
¿La relación entre estas dos seria que el cliente debe ser capaz de poder comprar cosas en la librería?La relación entre Librería y Cliente sería de agregación (una librería contiene cero o mucho clientes). El enunciado dice explícitamente que la librería tiene clientes y que los administra, por lo que, si modelaste una clase Librería, debería existir esta relación. Extendiendo la especificación, se agregaría el supuesto de que si el cliente no fue previamente agregado a la librería no puede realizar ninguna compra.
¿Deberia haber hecho una clase Ventas, la cual contenga muchas intancias de Venta, conectándolas con un rombo sin relleno, por ejemplo?No, en general tené mucho cuidado con los nombres de las clases en plural. Una clase que represente solamente a un contenedor de otros objetos sin ninguna responsabilidad agregada por lo general no tiene mucha razón de ser y alcanza con usar una colección genérica. A lo que me refería con mi comentario era sobre relaciones que ya tenía en tu diagrama. Por ejemplo, si en tu modelo un cliente puede tener muchas suscripciones y se las guarda en una colección, sería más específico que dibujaras esa relación como agregación.
La consigna dice en una parte 'El kiosco necesita como funcionalidad saber para un determinado mes cuánto debe cobrarle a cada cliente, y lo mismo para un año entero', yo lo que entendi que para un determinado mes, debía devolver cuánto hacía falta cobrarle a cada cliente. En la imagen por ahi no quedo claro, pero arriba del mensaje puse que devolvía totales y clientes, que es el total para cada cliente en un determinado mes. La culpa es mía por no haber puesto el nombre del diagrama, y qué accion realiza.OK, pero no se entiende cómo tu modelo hace eso. Más allá de la falta de detalles en los diagramas, me huele a que en tu modelo hacés cosas raras con el parámetro del mes que violan varios principios de diseño. Se resolvería de una manera más elegante modelando una clase CuentaCorriente (o similar) que se encargue de eso y es algo más similar a la realidad.
En mi modelo la clase cliente tiene los productos que compró en un determinado mesEl problema es que en el diagrama solamente ponés una relación entre Cliente y Producto y no se sabe qué pasa con ese mes que mencionás. Igual insisto que acá el problema muy probablemente sea también del modelo además de los diagramas.
'Anual(suscripcion) pregunta si la suscripción es anual. De ser así, aplica un descuento del 20% sobre la suscripción.Este es el problema más grave de todo el modelo. Estás explícitamente violando el encapsulamiento del objeto suscripción al preguntarle de qué tipo es y en función de eso realizar una determinada acción. En un parcial implicaría la desaprobación automática independientemente de que lo demás esté perfecto (sería como mencionar "gradiente extendido" en un examen de Análisis Matemático II). Acá es justamente donde se espera una solución polimórfica que no dependa de un if. Revisá la lectura Replace Conditional with Polymorphism.
Debería haber una agregación de instancias de Suscripcion unidas a Suscripciones, me parece.No, directamente no debería existir la clase Suscripciones en ningún diagrama. Te alcanza con Suscripción en singular y una relación de agregación con la clase que contenga las suscripciones.
Saludos,
Tomás
Hola! Volvi a hacer los modelos, esta vez siguiendo las indicaciones que me dijiste con respecto a los plurales y las relaciones entre los objetos:
Diagrama de Secuencia:
Boceto de diagrama de clases:
Hola Rodrigo:
Esta segunda versión de los diagramas se ve mejor pero sigue habiendo algunos inconvenientes.
En primer lugar, el diagrama de clases sigue mostrando muy poca información. Recordá que cada caja debería estar dividia en tres secciones horizontales, una para el nombre, otra para los atributos y otra para los métodos. Como mencionaba antes (y como también dice Martin Fowler), no es necesario que detalles absolutamente todo, pero tampoco está bien que no muestres absolutamente nada. Deberías incluir por lo menos algún método en cada clase (los que usás en el diagrama de secuencia, por ejemplo) como para que se entienda la responsabilidad principal de la clase en tu modelo. En especial, si aparece en tu modelo la relación de herencia es porque debés tener algún motivo muy válido para usarla y debe quedar evidenciado.
Revisá también los rombos de las agregaciones que hay algunos que están al revés.
Hay algunos métodos en el diagrama de secuencia que no son lo suficientemente descriptivos (por ejemplo "calcular" y "total").
Hay un error en la primera caja de iteración. Estás iterando por cada compra, luego por cada producto y luego por cada descuento y en cada caso preguntás un precio pero nunca hacés una suma. De manera tal que cuando termine la iteración solamente vas a tener el último precio dado que no guardaste los anteriores. El lugar en donde pongas esa suma es importante para saber si estás violando o no el principio "Tell, don't ask". A juzgar por el nombre del mensaje que Producto le envía a DescuentoRegular, da la sensación que violás el encapsulamiento. Ese mensaje se llama "calcular()" y no lleva ningún parámetro. ¿Cómo va a hacer el Descuento para calcular algo si no recibe nada con lo que tenga calcular? ¿Será que es un simple getter y el cálculo se realiza en otra clase? En cualquiera de los casos el modelo no sería adecuado.
Saludos,
Tomás