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.
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)
}