UTNianos

Versión completa: [FINAL][2015/10/01] Resolución
Actualmente estas viendo una versión simplificada de nuestro contenido. Ver la versión completa con el formato correcto.
Gente:
Estoy intentando realizar el final del 01/10/2015 de Diseño de Sistemas, y no estoy muy encaminado. Si alguien me puede dar una mano con esto, se los agradecería!

Primera Parte: Metodología
Spoiler: Mostrar

La empresa rusa aparentemente utilizó una metodología orientada a la documentación, secuencial y predictiva. El problema que tienen estos enfoques es que te llevan a tener una documentación extensa, que trata de abarcar todas las posibles cuestiones que puedan surgir durante el desarrollo y que no vuelven sobre el análisis y el diseño. Lo que puede llevar a casos como este donde se realiza una documentación extensa pero que a la hora de desarrollar no sirvió, porque evidentemente estaba mal enfocada, cosa que con una metodología iterativa no hubiera pasado, o que si durante la documentación se hubieran tirado algunas líneas de código se podrían haber detectado problemas sobre el documento.


Segunda Parte: Diseño en Objetos
Spoiler: Mostrar

1) Se utiliza el patrón Observer. Hormiga tiene una lista de SeguidorActividad (ReclamarAlimento, ActualizarEstadistica, PedirRegresar ó MandarHormigas (depende cómo se haya configurado)) a los que les informa cuando recoleta alimentos y está al límite o cuando ataca.

2) Hay un problema con las clases observadoras, ya que cada una de estas clases debe redefinir los métodos de la interfaz y cada clase solamente necesita un de los dos métodos que tiene la interfaz (“fueAtacada” y “estaAlLimite”), por lo que deja los métodos vacíos. Por ejemplo ReclamarAlimento no necesita el método “fueAtacada”.

De la forma que está solucionado se tiene en una lista de Seguidores tanto a las clases para los alimentos: ReclamarAlimento, ActualizarEstadistica, como las clases para las peleas, la cual depende de cómo se la haya configurado ya que puede tener PedirRegresar, MandarHormigas o ninguna. También pueden tener dos listas, una de seguidoresAlimento y otra de seguidoresPelea (que en realidad tiene un solo objeto). De cualquiera delas dos formas no está bien que las clases tengan métodos que no les pertenecen por el hecho de pertenecer a una misma interfaz.

Suponiendo con una lista, cuando una pelea a las clases seguidores de comida les va a llegar la notificación, pero como sus métodos están vacíos, no van a hacer nada..

Código con una lista. Con dos sería igual salvo que seguidores se transformaría en seguidoresComida y seguidoresPelea.
Clase Hormiga
var list<SeguidorActividad> seguidores = newArrayList

def recolectar(unaCantidad)
{
This.recolecta(unaCantidad); //Hace lo suyo

If this.estaAlLimite() //Devuelve true o false
{
this.seguidores.forEach[seguidor | seguidor.estaAlLimite(this)] // Una lista
}
}

def atacar(unAtacante)
{
This.ataca(unAtacante); //Hace lo suyo

this.seguidores.forEach[seguidor | seguidor.fueAtacada(this,unAtacante)]
}

Por lo que se ve las clases observadoras (sacando ActualizarAlimento) al recibir una notificación, le responden automáticamente a la hormiga lo que quiere el hormiguero utilizando sus métodos. Lo mejor es que se le avise al hormiguero que está llena (usando una lista de hormigas llenas), y este le responda en otro momento. Porque decirle al hormiguero “estoy llena” para saber que instantáneamente le va a decir “regresá” no tiene mucho sentido. Para eso, que cuando esté llena, directamente vaya al hormiguero.

Clase ActualizarAlimento
override estaAlLimite(Hormiga unaHormiga)
{
this.contabilizarSaraza(unaHormiga);
}

override fueAtacada(Hormiga unaHormiga, Atacante unAtacante){}

Clase ReclamarAlimento
override estaAlLimite(Hormiga unaHormiga)
{
super.pedirRegresar(unaHormiga);
}

override fueAtacada(Hormiga unaHormiga, Atacante unAtacante){}


Clase PedirRegresar

override fueAtacada(Hormiga unaHormiga, Atacante unAtacante)
{
super.pedirRegresar(unaHormiga);
}

override estaAlLimite(Hormiga unaHormiga){}

Clase MandarHormigas

override fueAtacada(Hormiga unaHormiga, Atacante unAtacante)
{
super.pedirRegresar(unaHormiga);
}

override estaAlLimite(Hormiga unaHormiga)

Esto que está en rojo no sé si viendo el diagrama puedo afirmar que es así o que faltan métodos. Si para el final no tengo que tenerlo en cuenta, ya que no sé cómo están hechos los métodos en el Hormiguero.


Por otra parte, en el tema peleas, el enunciado deja en claro que el hormiguero le responde a todas las hormigas por igual, pero esto puede cambiar. Y en solución del enunciado, cada hormiga sabe qué hacer porque le avisa al seguidor correspondiente. No es responsabilidad de la hormiga saber que le va a responder el hormiguero. Y si se quiere modificar al hormiguero, es necesario cambiar a todas las hormigas para un próximo ataque.

La solución que planteo es utilizar un patrón State para el Hormiguero, ya que dependiendo del estado que tenga es la respuesta que se le va a dar a la hormiga. Se agrega la clase “NoResponder”. De esta forma es necesario que la hormiga conozca al hormiguero cosa que antes, las clases observadoras usaban el super para utilizar los métodos de Hormiguero.

En el Observer se mantienen ReclamarAlimento y ActualizarEstadísticas, que ambas están relacionadas con la comida y entiende el mensaje “estaAlLimite”. Se logra una mayor cohesión que antes ya que la responsabilidad es solo la de los alimentos. Antes tenía métodos de pelea aunque estuviesen vacíos, y estos no son de su responsabilidad.

En cuanto a la clase alimentos, en la solución planteada, no es flexible si se quiere agregar otro ya que sería necesario crear un método por cada alimento nuevo, y la clase terminaría teniendo métodos iguales en los que solo cambia la cantidad de alimento útil. Lo que difiere de los alimentos es la cantidad de gramos del alimento útil, por lo que no es necesario establecer una herencia con clases hijas como “Galletita”, “Miga”, "Helado", "Alafjor", etc, ya que el comportamiento de cada alimento el mismo, solo cambia la cantidad de “alimento_utill”. La solución que planteo es agregar un atributo que se llame “nombre” y mediante un constructor establecer la cantidad de alimento útil con la que comienza cada objeto Alimento. Los métodos “nueva_galletita()” y “nueva_miga()” no hacen falta.
Por otra parte se da la responsabilidad al Alimento de decirle a la hormiga que extraiga comida, cuando debería ser al revés. La hormiga elige el alimento que desea extraer. La responsabilidad del alimento es disminuir su cantidad cuando una hormiga lo come y solamente informarle a la hormiga cuánto alimento útil tiene. Por lo que recolectar(cantidad_alimento) debe ser recolectar(alimento), y extraer la cantidad que pueda según la capacidad disponible. Y el método extraer_alimento(hormiga) debe ser extraer_alimento(cantidad_alimento). La hormiga le devuelve cuanto alimento pudo sacar para que el alimento reduzca su cantidad.

[attachment=11917]

Clase Alimento
new (String unNombre, int unaCantidad)
{
This.nombre = unNombre
This.alimento_util = unaCantidad
}

def extraer_alimento(int alimento_recolectado)
{
This.alimento_util = this.alimento_util - alimento_recolectado
}


Clase Hormiga
var list<SeguidorActividad> seguidores = newArrayList
var Hormiguero hormiguero
var capacidadMaxima
var cantidadAlimento

def recolectar(unAlimento)
{
var int cantidad_extraida

if (this.cantidadAlimento + unAlimento.alimento_util >= capacidadMaxima)
{
cantidad_extraida = this.capacidadMaxima - this.cantidadAlimento
this.cantidadAlimento = this.capacidadMaxima
}
else
{
cantidad_extraida = unAlimento.alimento_util
this.cantidadAlimento = this.cantidadAlimento + unAlimento.alimento_util
}

unAlimento.extraer_alimento(cantidad_extraida)

If this.estaAlLimite() //Devuelve true o false
{
this.seguidores.forEach[seguidor | seguidor.estaAlLimite(this)] // Una lista
}
}


def atacar(unAtacante)
{
this.ataca(unAtacante); //Hace lo suyo
this.hormiguero.responderAtaque(this)

}

Clase Hormiguero
var estadoAtaque estadoAtaque
def repsonderAtaque (Hormiga unaHormiga)
{
estadoAtaque.manejarAtaque(unaHormiga)
}


Tercera Parte: Persistencia
Spoiler: Mostrar
Todavía no lo vi.
Leandro, yo también estuve tratando de hacer este parcial.

Metodología
Spoiler: Mostrar
Yo pondría que la empresa rusa aparentemente utilizó una metodología orientada a la documentación, secuencial y predictiva. El problema que tienen estos enfoques es que te llevan a tener una documentación extensa, que trata de abarcar todas las posibles cuestiones que puedan surgir durante el desarrollo y que no vuelven sobre el análisis y el diseño. Lo que puede llevar a casos como este donde se realiza una documentación extensa pero que a la hora de desarrollar no sirvió, porque evidentemente estaba mal enfocada, cosa que con una metodología iterativa no hubiera pasado, o que si durante la documentación se hubieran tirado algunas líneas de código se podrían haber detectado problemas sobre el documento.
Además lo veo relacionado con el concepto de fail fast, que es justamente lo contrario a lo que hicieron acá.

Diseño
Spoiler: Mostrar
Yo hubiera dejado las acciones para cuando llega al tope del alimento como observers en la hormiga. Esto le da más flexibilidad a la hora de agregar o sacar acciones para este caso de uso, y no le veo ninguna contra. Además como para cada hormiga son las mismas acciones, no complica la instanciación y te deja la puerta abierta para poder agregar acciones particulares a cada hormiga más adelante.
En cambio, para cuando es atacada, lo puse como un atributo configurable del hormiguero (strategy), y agregando la acción que no hace nada que faltaba. Para este caso solo se puede configurar una acción.
Fer, gracias!

Muy bueno lo de Metodología. No sabía por dónde encararlo.

La parte del observer, como la harías? el observador es el hormiguero y la hormiga le avisa cuando está llena? Cada hormiga tendría un solo observador. La idea del observer es que el observable (o sujeto) tenga varios observadores.

Lo que no entiendo es, la hormiga le avisa que está llena al hormiguero y este instantáneamente le avisa que vuelva?

Lo que podría hacerse con el observer es que cuando la hormiga le avisa que está llena, el hormiguero la agregue a la lista de observadores, y en x momento, le avisa a todas que vuelvan.

Algo así sería el método

>> hormiga
estoyllena(hormiguero)
{
hormiguero.volve(this)
}

Y lo del strategy lo tendría el hormiguero? pero así el hormiguero le respondería a todas las hormigas lo mismo según lo que tenga configurado, no?
La parte del observer la dej[e tal cual estaba, pero sacando las clases PedirRegresar y MandarHormigas, de esta manera la interfaz SeguidorActividad solo va a tener 1 método, que va a ser implementado por ReclamarAlimento y ActualizarEstadísticas. Las otras dos las hice implementar otra interfaz y va 1 sola como atributo del hormiguero.
El tema es que ahora la hormiga va a necesitar conocer su hormiguero, cosa que antes con los observers no hacía falta, los que lo conocían eran los observers.

>>Hormiga
def esta_al_limite(){
seguidoresActividad.foreach[ obs | obs.notify(this) ]
}

def atacar(atacante){
....
hormiguero.ayudar(this)
}

>>Hormiguero | atributos: IDefensa defensa
def ayudar(Hormiga hormiguita){
defensa.execute(this)
}

IDefensa es la interfaz que implementan MandarHormigas, PedirRegresar y NoHacerNada


De mi solución no me termina de convencer que la hormiga tenga que conocer su hormiguero, otra alternativa sería pedirle a un repositorio o home de hormigueros que me de el hormiguero al cual pertenece tal hormiga, pero tampoco me cierra del todo.
1) Ah, los obervadores son "pedir regresar" y "estadísticas". Perfecto.
Pedir regresar le avisa al hormiguero. Y el hormiguero que hace o eso no hace falta tenerlo en cuenta?

2) Viendo el enunciado queda claro que el hormiguero le responde a todas por igual, así que está bien que sea haga mediante un State/Strategy.

Cita:Cuando esto ocurre, la hormiga pide al hormiguero que la defienda; dependiendo de cómo lo hayamos configurado inicialmente, éste podrá reaccionar de una de las siguientes maneras: ignorando el pedido, pidiéndole a la hormiga que regrese o mandando hormigas en su ayuda.

El de ayudar no tendría que ser "hormiguita" en vez de "this"?

>>Hormiguero | atributos: IDefensa defensa
def ayudar(Hormiga hormiguita){
defensa.execute(hormiguita)
}


Cambié la solución con las cosas que me fuiste diciendo. Y agregué sobre la clase Alimento que estaba mal.
Mira, acabo de encontrar esto. Parcial hormigas
Lo que está en la parte que dice Rama 2 es bastante parecido a lo que planteaba yo. Salvo que no mantiene los observers, solo una referencia a la interfaz de estadísticas.

Espero te sirva.
Saludos
(11-12-2015 20:53)fer89cai escribió: [ -> ]Mira, acabo de encontrar esto. Parcial hormigas
Lo que está en la parte que dice Rama 2 es bastante parecido a lo que planteaba yo. Salvo que no mantiene los observers, solo una referencia a la interfaz de estadísticas.

Espero te sirva.
Saludos


Sí, lo estuve viendo con Cyphius y tengo cambios para hacer.

"Cuando una hormiga extrae alimento, lo coloca sobre espalda, y si quedan al límite de su carga (10g de alimento), deben depositarla en el hormiguero. A fines estadísticos, también queremos actualizar cuántas veces ocurrió esto."

Es lo que decía yo, no tenía sentido que la hormiga le avise al hormiguero para que este la haga ir, directamente tiene que llevar el alimento al hormiguero. Para eso, debe conocerlo. Entonces no hace falta la clase "Reclamar alimento".

Por otro lado las subclases "miga" y "galletitas" que yo las pondría como atributos, está bien que sean subclases ya que esto es un juego y tiene una representación gráfica. Todas las migas se van a dibujar de la misma manera.

Y los de las estadísticas, Cyphius usaría una clase enum, para agregar diferentes eventos. Mantendría el observer para que cada observador cuando sea notificado se fije si es un evento al que le corresponde llevar estadísticas.

No entiendo la flecha a SistemaEstadísticas, quiere decir que tiene un atributo que es esa interfaz o que extiende a la interfaz? Si es el primer caso necesita clases que implementen los métodos como hace Modo Defensa, y si fuese que extiende no tendría que ser con línea punteada?
Lo de la flecha a sistema de estadisticas es una simplicacion.

Antes usaba observer para pasar los datos. Como solo le quedo un observer y tenia ¿3? clases la implementacion, lo hizo mas simple y lo manda directo de una. Por eso la flecha hormiga que conoce * observer pasa a ser conozco un sistema de estadistica.
(11-12-2015 21:17)Cyphius escribió: [ -> ]Lo de la flecha a sistema de estadisticas es una simplicacion.

Antes usaba observer para pasar los datos. Como solo le quedo un observer y tenia ¿3? clases la implementacion, lo hizo mas simple y lo manda directo de una. Por eso la flecha hormiga que conoce * observer pasa a ser conozco un sistema de estadistica.

Si lo conoce, es un atributo de la clase, y entonces la interfaz tendría que tener subclases que implementen los métodos de la interfaz. Si no, no hace nada eso, la interfaz tiene métodos abstractos nomás. Supongo que es este caso, pero no se gastaron en poner las subclases.

Y si la implementa, tendría que ser punteada y redefinir los métodos en su clase.
La hormiga tiene un atributo que es de clase SistemaEstadistica. Lo conoce

SistemaEstadistica es una interface no se puede instanciar, lo que no se muestra "" y no interesa en la abstraccion. Es que existe alguna clase que implementa esa interfaz, a uno solo le interesa que sea un SistemaEstadistica.

Cuando se instancia la hormiga, esta va a tener es su constructor o se le asignara la instancia de clase que implementa sistemaEstadistica. Como va a ser un sistemaEstadistica hormiga puede llamar a los métodos que estan declarados en la interfaz en este caso contabilizarZarasa.

El diagrama es de una parte del juego, no es el diagrama de juego. Para lo mismo se esta haciendo una abstraccion porque lo que interesa es conocer el comportamiento de hormiga/hormiguero la estadística es parte del juego y esta fuera del dominio que interesa analizar por eso que solo muestran la interfaz. O por lo menos asi pienso porque lo mostraron asi.
URLs de referencia