Práctica de Diseño (Ingeniería I)

De Cuba-Wiki
Revisión del 21:53 24 nov 2007 de 24.232.252.2 (discusión) (→‎Ejercicio 19)
(difs.) ← Revisión anterior | Revisión actual (difs.) | Revisión siguiente → (difs.)

Introducción: Sobre las herramientas de diseño

Ejercicio 1

Ejercicio 2

Ejercicio 3

Uso de las herramientas durante el proceso de diseño

Ejercicio 4

Ejercicio 5

Ejercicio 6

Ejercicio 7

Ejercicio 8

Ejercicio 9

Ejercicio 10

Ejercicio 11

Ejercicio 12

Ejercicio 13

Principios de diseño: Acoplamiento, Cohesión, Patterns

Ejercicio 14

Ejercicio 15

Contamos una interfaz con muchos métodos (fat interface) que es consumida por muchos clientes:

ClienteA solo utiliza el método showData(), ClienteB solo utiliza parseDate() y ClienteC solo readData().

Al tiempo de haber publicado nuestra interfaz FatServices, el ClienteC además de leer datos necesita un servicio para validarlos, entonces pide agregar un nuevo método validateData().

  • Qué problemas encuentra al enfoque propuesto,
  • ¿Qué principio o principios se están violando?
  • Proponga un diseño alternativo

Respuesta:

Hay un alto grado de fan-in sobre la interfaz FatServices, es decir, es utilizada por muchos clientes, y contiene muchos métodos distintos, lo que genera un alto acoplamiento.

Al añadir un nuevo método requerido por un cliente, se obliga a todas las clases subyacentes que implementan dicha interfaz a implementar este nuevo método, clases que tal vez no son usadas por el cliente C.

Lo más conveniente es partir la FatServices en varias interfaces para modularizar el uso de cada una por parte de los clientes. Es decir, una interfaz por método en este caso, lo que ayuda al acoplamiento aunque lastima a la cohesión.

Ejercicio 16

Ejercicio 17

Eustaquio el burrero (un amigo de la casa), nos pidió un pequeño sistema que le permitiera comparar distintos caballos para poder determinar a cuál debía jugarle. En principio nos dijo que una estrategia que le funcionaba, era apostar a los caballos más jóvenes.

Luego de pensar unos minutos, le entregamos la siguiente solución:

Arrays.sort() es un método de la clase Array que permite ordenar arrays de objetos Comparables (que implementan la interfaz Comparable).

Eustaquio probó la solución, y se fue más que contento.

Una semana después, nuestro querido amigo volvió a charlar con nosotros, su táctica de apostar al más joven no estaba funcionando. Quería ahora, además de poder ordenar por edad, ordenar por peso (había algunos caballitos jóvenes que estaban siendo muy bien alimentados y estaban un poco rechonchos).

Ningún problema dijimos, es muy simple…o no?

  • ¿Cómo se le ocurre solucionar el problema que nos plantea Eustaquio?
  • ¿Violamos algún principio de diseño en nuestra primera solución?
  • ¿Cuánto le cuesta a su solución propuesta en 1) permitir comparar

también caballos por altura?

Respuesta:

La idea es implementar un strategy pattern para determinar como se comparan caballos. Hay dos alternativas.

Una posibilidad es modificar Arrays.Sort() para que en lugar de recibir objetos Comparables, también soporte recibir objetos que no implementen dicha interfaz, pero como parámetro adicional reciba un Comparer, que tome dos objetos de una determinada instancia y los compare. Así, se tendrían PesoComparer, AlturaComparer y EdadComparer, que implementan Comparer, y la strategy se haría en el método sort.

Si el método sort ya está cerrado, el pattern puede implementarse en el caballo mismo. Se mantiene la estructura de Comparers de la versión anterior, pero en lugar de pasarlo a Arrays.Sort() como parámetro, se lo asigna al mismo Caballo en un setter. El caballo, entonces, cuando quiera implementar el método CompareTo, simplemente llama al método CompareTo del Comparer que tenga asignado.

Y en mi opinion personal (Palla), no se violo ningun principio de diseño en la solucion inicial, de hecho, esta perfecta. El refactor para incluir los patterns necesarios no debe hacerse hasta que sea necesario. Pero eso no es lo que tiene que ir como respuesta aca! Deberia ir alguna paparruchada como que existe un alto grado de acoplamiento ya que un caballo no tiene por qué tener incorporado el conocimiento de cómo compararse que puede responder a distintos criterios variables.

Ejercicio 18

¿Qué patrones de diseño ayudan a cumplir OCP en un diseño?

Respuesta:

¿Qué significa OCP? Google contestó lo siguiente:

  • Oral Contraceptive Pill
  • Open Core Protocol
  • Original Coated Paper
  • Official Community Plan
  • Optical Communication Products
  • Omni Consumer Products (la compañia que hizo a Robocop!)

Aparentemente es Open-Closed Principle, que dice que las software entities should be open for extension, but closed for modification (artículo sobre el tema).

  • La factory permite desacoplar la construcción de un determinado objeto dependiendo del contexto. La factory en sí obliga a modificar sus métodos al aparecer una nueva clase concreta del individuo que se crea, con lo que va contra este principio. La abstract factory lo salva, creando una nueva factory para cada conjunto de objetos relacionados a un mismo contexto; pero sigue siendo necesario algún lugar donde se haga el case por contexto (una factory de factories?).
  • El adapter es justamente este principio. No modificar lo que ya existe sino agregarle una capa intermedia, extendiéndola para que pueda cumplir los pedidos de otros clientes.
  • La strategy también lo cumple, agregando estrategias nuevas sin tener que modificar los algoritmos preexistentes.
  • Al observer no le veo relación alguna.
  • El signleton mucho menos.
  • La facade permite abstraer a los clientes del manejo de un sistema complejo, pero no extiende funcionalidades, simplemente las remapea.

Ejercicio 19

Se tiene el siguiente diseño de un FileSystem:

El mismo permite modelar un sistema de directorios con sus archivos, y pedir la impresión de dicha lista por pantalla (dir). El siguiente código ejemplifica su uso:

public static void main(String[] args) 
{
   Directory myDoc = new Directory("My Documents");
   Directory pictures = new Directory("Pictures");
   Directory music = new Directory("MP3");
	
   File f1 = new File("Silvina_Luna.jgp");
   File f2 = new File("Pampita.jgp");
   pictures.add(f1);
   pictures.add(f2);
   myDoc.add(pictures);
	
   File f3 = new File("Los Palmeras – Bombón Asesino.mp3");
   music.add(f3);
   myDoc.add(music);
   myDoc.dir();
}

El resultado esperado es:

Directory name: My Documents
Directory name: Pictures
File name: Silvina_Luna.jgp
File name: Pampita.jgp
Directory name: MP3
File name: Los Palmeras – Bombón Asesino.mp3
  • ¿Qué problemas encuentra en el diseño propuesto?
  • Proponga una solución alternativa.

Respuesta:

Existe una funcionalidad común entre los dos objetos que es imprimirse por pantalla. El llamador (dir) debe castear a cada una de sus entradas al objeto que corresponda para llamar al mismo método. Como todo lo que implique casteo en general es feo, conviene extraer el comportamiento comun a una interfaz, digamos IImprimible (si, me gustan las interfaces à la NET, con la capital I adelante).

La solución consistiría en agregar dicha interfaz, con un único método dir o ls (está con dos nombres distintos en el enunciado) y que cada clase lo implemente de manera distinta. File debe solamente mostrar su nombre; Directory debe mostrar su nombre y llamar al método ls de todas sus entradas.

También hay que modificar el método Add de de directory para que solo tome IImprimibles.

Ejercicio 20