Difference between revisions of "Introdução aos Objectos/Exercício 02: Energia"

From Wiki**3

< Introdução aos Objectos
(Programa Principal)
(Conceito de Gato)
 
(17 intermediate revisions by the same user not shown)
Line 18: Line 18:
 
Considere que a energia disponível inicialmente para os cães, gatos, pássaros e ratos é, respectivamente, de 1000, 500, 20 e 50 unidades. Quando os animais correm, gastam, respectivamente, 50, 25, 5 e 2 unidades. Um pássaro, quando voa, gasta apenas 2 unidades. Um cão que ataque um gato gasta 100 unidades e faz com que o gato perca 25.
 
Considere que a energia disponível inicialmente para os cães, gatos, pássaros e ratos é, respectivamente, de 1000, 500, 20 e 50 unidades. Quando os animais correm, gastam, respectivamente, 50, 25, 5 e 2 unidades. Um pássaro, quando voa, gasta apenas 2 unidades. Um cão que ataque um gato gasta 100 unidades e faz com que o gato perca 25.
  
Para um predador comer uma presa tem de a perseguir para a capturar (podendo a perseguição ser ou não bem sucedida). Um cão consegue capturar um rato em cada 25 tentativas. Para os gatos, o rácio é 1 em 5 (ratos) e 1 em 10 (pássaros). A perseguição consome a mesma energia que correr (para cada interveniente), mas a presa recebe um bónus de 5 unidades se escapar. Se a presa estiver a dormir, é apanhada 1 em cada 2 tentativas.
+
Para um predador comer uma presa tem de a perseguir para a capturar (podendo a perseguição ser ou não bem sucedida). Um cão consegue capturar um rato em cada 25 tentativas. Para os gatos, o rácio é 1 em 5 (ratos) e 1 em 10 (pássaros). A perseguição consome a mesma energia que correr (para cada interveniente), mas a presa recebe um bónus de 5 unidades se escapar. <!--Se a presa estiver a dormir, é apanhada 1 em cada 2 tentativas.-->
  
 
Construa uma aplicação onde existem 2 cães ("Piloto" e "Átila"), 3 gatos ("Tareco", "Pantufa" e "Kitty"), 20 pássaros e 50 ratos (os pássaros e ratos podem ser organizados em arrays).
 
Construa uma aplicação onde existem 2 cães ("Piloto" e "Átila"), 3 gatos ("Tareco", "Pantufa" e "Kitty"), 20 pássaros e 50 ratos (os pássaros e ratos podem ser organizados em arrays).
Line 36: Line 36:
 
Neste caso, como se escolheu (dada a fase introdutória) não representar os conceitos hierarquizados, apenas existem relações de dependência entre as classes (representadas pelas setas a tracejado).
 
Neste caso, como se escolheu (dada a fase introdutória) não representar os conceitos hierarquizados, apenas existem relações de dependência entre as classes (representadas pelas setas a tracejado).
  
 +
Existem opções de desenho sub-óptimas. Consegue detectá-las?
 +
 +
BUG: o método "escaped" deveria ter como tipo de retorno "void" e não "boolean".
 +
 +
{{CollapsedCode|Diagrama de classes|
 
[[Image:PO-dog-cat-mouse-bird-energy-noinheritance.png]]
 
[[Image:PO-dog-cat-mouse-bird-energy-noinheritance.png]]
 +
}}
  
There are several suboptimal design choices. Can you spot them?
+
== Conceito de Cão ==
 
 
== Conceito de Cão: Dog ==
 
  
<java5>
+
{{CollapsedCode|Ficheiro '''Dog.java'''|
 +
<source lang="java">
 
public class Dog {
 
public class Dog {
  
/**
+
    /**
* We define the base energy as a constant, but it does not have to be this
+
    * We define the base energy as a constant, but it does not have to be this
* way. It could be defined differently for each dog (but this requirement
+
    * way. It could be defined differently for each dog (but this requirement
* does not exist in this case).
+
    * does not exist in this case).
*/
+
    */
private static final int BASE_ENERGY = 1000;
+
    private static final int BASE_ENERGY = 1000;
  
/**
+
    /**
* The dog's name.
+
    * The dog's name.
*/
+
    */
private String _name;
+
    private String _name;
  
/**
+
    /**
* The dog's current energy value.
+
    * The dog's current energy value.
*/
+
    */
private int _energy = BASE_ENERGY;
+
    private int _energy = BASE_ENERGY;
  
/**
+
    /**
* Initialize a dog with a name. Default energy levels are used.
+
    * Initialize a dog with a name. Default energy levels are used.
*  
+
    *
* @param name
+
    * @param name
*/
+
    */
public Dog(String name) {
+
    public Dog(String name) {
_name = name;
+
        _name = name;
}
+
    }
  
/**
+
    /**
* @return dog's current energy level.
+
    * @return dog's current energy level.
*/
+
    */
private int getEnergy() {
+
    public int getEnergy() {
return _energy;
+
        return _energy;
}
+
    }
  
/**
+
    /**
* @return dog's name
+
    * @return dog's name
*/
+
    */
public String getName() {
+
    public String getName() {
return _name;
+
        return _name;
}
+
    }
  
/**
+
    /**
* Set the dog's name
+
    * Set the dog's name
*  
+
    *
* @param name
+
    * @param name
*            the dog's name
+
    *            the dog's name
*/
+
    */
public void setName(String name) {
+
    public void setName(String name) {
_name = name;
+
        _name = name;
}
+
    }
  
/**
+
    /**
* When a dog runs, the energy decreases by 50 units. This value could be
+
    * When a dog runs, the energy decreases by 50 units. This value could be
* defined as an attribute or as a constant.
+
    * defined as an attribute or as a constant.
*  
+
    *
* @return whether the dog was able to run.
+
    * @return whether the dog was able to run.
*/
+
    */
public boolean run() {
+
    public boolean run() {
if (_energy < 50)
+
        if (_energy < 50)
return false;
+
            return false;
_energy -= 50;
+
        _energy -= 50;
return true;
+
        return true;
}
+
    }
  
/**
+
    /**
* Call "run" to account for spent energy.
+
    * Call "run" to account for spent energy.
*  
+
    *
* @param mouse
+
    * @param mouse
*            the mouse to be chased.
+
    *            the mouse to be chased.
* @return whether the dog was able to catch the mouse. If the mouse
+
    * @return whether the dog was able to catch the mouse. If the mouse
*        escapes, its energy increases.
+
    *        escapes, its energy increases.
*/
+
    */
public boolean caughtMouse(Mouse mouse) {
+
    public boolean caughtMouse(Mouse mouse) {
run();
+
        run();
mouse.run();
+
        mouse.run();
if (0 == (int) (25 * Math.random())) {
+
        if (0 == (int) (25 * Math.random())) {
return true;
+
            return true;
}
+
        }
mouse.escaped();
+
        mouse.escaped();
return false;
+
        return false;
}
+
    }
  
/**
+
    /**
* Eating is more or less like a vampire feeding...
+
    * Eating is more or less like a vampire feeding...
*  
+
    *
* @param mouse
+
    * @param mouse
*            the mouse to eat.
+
    *            the mouse to eat.
*/
+
    */
public void eatMouse(Mouse mouse) {
+
    public void eatMouse(Mouse mouse) {
if (caughtMouse(mouse))
+
        if (caughtMouse(mouse))
_energy += mouse.drain();
+
            _energy += mouse.drain();
}
+
    }
  
/**
+
    /**
* We assume that the dog is always able to attack the cat. The parameter to
+
    * We assume that the dog is always able to attack the cat. The parameter to
* ''attacked'' is used to specify the amount of energy lost by the cat.
+
    * ''attacked'' is used to specify the amount of energy lost by the cat.
* Note that we are assuming that the degree of loss depends on the attacker
+
    * Note that we are assuming that the degree of loss depends on the attacker
* (hence the value being defined in the dog class).
+
    * (hence the value being defined in the dog class).
*  
+
    *
* The energy values could be defined as attributes or as constants.
+
    * The energy values could be defined as attributes or as constants.
*  
+
    *
* @param cat
+
    * @param cat
*            the cat the dog attacks
+
    *            the cat the dog attacks
*/
+
    */
public void attackCat(Cat cat) {
+
    public void attackCat(Cat cat) {
_energy -= 100;
+
        _energy -= 100;
cat.attacked(25);
+
        cat.attacked(25);
}
+
    }
  
/**
+
    /**
* Energy is recovered when sleeping.
+
    * Energy is recovered when sleeping.
*/
+
    */
public void sleep() {
+
    public void sleep() {
_energy = BASE_ENERGY;
+
        _energy = BASE_ENERGY;
}
+
    }
  
/**
+
    /**
* @see java.lang.Object#equals(java.lang.Object)
+
    * @see java.lang.Object#equals(java.lang.Object)
*/
+
    */
@Override
+
    @Override
public boolean equals(Object o) {
+
    public boolean equals(Object o) {
if (o instanceof Dog) {
+
        if (o instanceof Dog) {
Dog dog = (Dog) o;
+
            Dog dog = (Dog) o;
return _name.equals(dog.getName()) && _energy == dog.getEnergy();
+
            return _name.equals(dog.getName()) && _energy == dog.getEnergy();
}
+
        }
return false;
+
        return false;
}
+
    }
  
/**
+
    /**
* @see java.lang.Object#toString()
+
    * @see java.lang.Object#toString()
*/
+
    */
@Override
+
    @Override
@SuppressWarnings("nls")
+
    public String toString() {
public String toString() {
+
        return _name + " (dog) (" + _energy + ")";
return _name + " (dog) (" + _energy + ")";
+
    }
}
 
 
}
 
}
</java5>
+
</source>
 +
}}
  
== Conceito de Gato: Cat ==
+
== Conceito de Gato ==
  
<java5>
+
{{CollapsedCode|Ficheiro '''Cat.java'''|
 +
<source lang="java">
 
public class Cat {
 
public class Cat {
  
/**
+
    /**
* We define the base energy as a constant, but it does not have to be this
+
    * We define the base energy as a constant, but it does not have to be this
* way. It could be defined differently for each dog (but this requirement
+
    * way. It could be defined differently for each dog (but this requirement
* does not exist in this case).
+
    * does not exist in this case).
*/
+
    */
private static final int BASE_ENERGY = 500;
+
    private static final int BASE_ENERGY = 500;
  
/**
+
    /**
* The cat's name.
+
    * The cat's name.
*/
+
    */
private String _name;
+
    private String _name;
  
/**
+
    /**
* The cat's current energy value.
+
    * The cat's current energy value.
*/
+
    */
private int _energy = BASE_ENERGY;
+
    private int _energy = BASE_ENERGY;
  
/**
+
    /**
* Initialize a cat with a name. Default energy levels are used.
+
    * Initialize a cat with a name. Default energy levels are used.
*  
+
    *
* @param name
+
    * @param name
*/
+
    */
public Cat(String name) {
+
    public Cat(String name) {
_name = name;
+
        _name = name;
}
+
    }
  
/**
+
    /**
* @return cat's current energy level.
+
    * @return cat's current energy level.
*/
+
    */
private int getEnergy() {
+
    public int getEnergy() {
return _energy;
+
        return _energy;
}
+
    }
  
/**
+
    /**
* @return cat's name
+
    * @return cat's name
*/
+
    */
public String getName() {
+
    public String getName() {
return _name;
+
        return _name;
}
+
    }
  
/**
+
    /**
* Set the cat's name
+
    * Set the cat's name
*  
+
    *
* @param name
+
    * @param name
*            the cat's name
+
    *            the cat's name
*/
+
    */
public void setName(String name) {
+
    public void setName(String name) {
_name = name;
+
        _name = name;
}
+
    }
  
/**
+
    /**
* When a cat runs, the energy decreases by 25 units. This value could be
+
    * When a cat runs, the energy decreases by 25 units. This value could be
* defined as an attribute or as a constant.
+
    * defined as an attribute or as a constant.
*  
+
    *
* @return whether the cat was able to run.
+
    * @return whether the cat was able to run.
*/
+
    */
public boolean run() {
+
    public boolean run() {
if (_energy < 25)
+
        if (_energy < 25)
return false;
+
            return false;
_energy -= 25;
+
        _energy -= 25;
return true;
+
        return true;
}
+
    }
  
/**
+
    /**
* Call "run" to account for spent energy.
+
    * Call "run" to account for spent energy.
*  
+
    *
* @param mouse
+
    * @param mouse
*            the mouse to be caught.
+
    *            the mouse to be caught.
* @return whether the cat was able to catch the mouse. If the mouse
+
    * @return whether the cat was able to catch the mouse. If the mouse
*        escapes, its energy increases.
+
    *        escapes, its energy increases.
*/
+
    */
public boolean caughtMouse(Mouse mouse) {
+
    public boolean caughtMouse(Mouse mouse) {
run();
+
        run();
mouse.run();
+
        mouse.run();
if (0 == (int) (5 * Math.random())) {
+
        if (0 == (int) (5 * Math.random())) {
return true;
+
            return true;
}
+
        }
mouse.escaped();
+
        mouse.escaped();
return false;
+
        return false;
}
+
    }
  
/**
+
    /**
* Call "run" ("fly"??) to account for spent energy.
+
    * Call "run" ("fly"??) to account for spent energy.
*  
+
    *
* @param bird
+
    * @param bird
*            the bird to be caught.
+
    *            the bird to be caught.
* @return whether the cat was able to catch the bird. If the bird escapes,
+
    * @return whether the cat was able to catch the bird. If the bird escapes,
*        its energy increases.
+
    *        its energy increases.
*/
+
    */
public boolean caughtBird(Bird bird) {
+
    public boolean caughtBird(Bird bird) {
run();
+
        run();
bird.fly(); // run??
+
        bird.fly(); // run??
if (0 == (int) (10 * Math.random())) {
+
        if (0 == (int) (10 * Math.random())) {
return true;
+
            return true;
}
+
        }
bird.escaped();
+
        bird.escaped();
return false;
+
        return false;
}
+
    }
  
/**
+
    /**
* Eating is more or less like a vampire feeding...
+
    * Eating is more or less like a vampire feeding...
*  
+
    *
* @param mouse
+
    * @param mouse
*            the mouse to eat.
+
    *            the mouse to eat.
*/
+
    */
public void eatMouse(Mouse mouse) {
+
    public void eatMouse(Mouse mouse) {
if (caughtMouse(mouse))
+
        if (caughtMouse(mouse))
_energy += mouse.drain();
+
            _energy += mouse.drain();
}
+
    }
  
/**
+
    /**
* Eating is more or less like a vampire feeding...
+
    * Eating is more or less like a vampire feeding...
*  
+
    *
* @param bird
+
    * @param bird
*            the mouse to eat.
+
    *            the mouse to eat.
*/
+
    */
public void eatBird(Bird bird) {
+
    public void eatBird(Bird bird) {
if (caughtBird(bird))
+
        if (caughtBird(bird))
_energy += bird.drain();
+
            _energy += bird.drain();
}
+
    }
  
/**
+
    /**
* We should probably check for large decrease values. Nevertheless, in this
+
    * We should probably check for large decrease values. Nevertheless, in this
* case, for simplicity, we will let the energy go negative and, later on,
+
    * case, for simplicity, we will let the energy go negative and, later on,
* the cat can recover after a nap.
+
    * the cat can recover after a nap.
*  
+
    *
* @param energyDecrease
+
    * @param energyDecrease
*/
+
    */
public void attacked(int energyDecrease) {
+
    public void attacked(int energyDecrease) {
_energy -= energyDecrease;
+
        _energy -= energyDecrease;
}
+
    }
  
/**
+
    /**
* Energy is recovered when sleeping.
+
    * Energy is recovered when sleeping.
*/
+
    */
public void sleep() {
+
    public void sleep() {
_energy = BASE_ENERGY;
+
        _energy = BASE_ENERGY;
}
+
    }
  
/**
+
    /**
* @see java.lang.Object#equals(java.lang.Object)
+
    * @see java.lang.Object#equals(java.lang.Object)
*/
+
    */
@Override
+
    @Override
public boolean equals(Object o) {
+
    public boolean equals(Object o) {
if (o instanceof Cat) {
+
        if (o instanceof Cat) {
Cat cat = (Cat) o;
+
            Cat cat = (Cat) o;
return _name.equals(cat.getName()) && _energy == cat.getEnergy();
+
            return _name.equals(cat.getName()) && _energy == cat.getEnergy();
}
+
        }
return false;
+
        return false;
}
+
    }
  
/**
+
    /**
* @see java.lang.Object#toString()
+
    * @see java.lang.Object#toString()
*/
+
    */
@Override
+
    @Override
@SuppressWarnings("nls")
+
    public String toString() {
public String toString() {
+
        return _name + " (cat) (" + _energy + ")";
return _name + " (cat) (" + _energy + ")";
+
    }
}
 
 
}
 
}
</java5>
+
</source>
 +
}}
  
== Conceito de Rato: Mouse ==
+
== Conceito de Rato ==
  
<java5>
+
{{CollapsedCode|Ficheiro '''Mouse.java'''|
 +
<source lang="java">
 
public class Mouse {
 
public class Mouse {
  
Line 377: Line 384:
 
   * When a mouse runs, the energy decreases by 2 units. This value could be
 
   * When a mouse runs, the energy decreases by 2 units. This value could be
 
   * defined as an attribute or as a constant.
 
   * defined as an attribute or as a constant.
   *  
+
   *
 
   * @return whether the mouse was able to run.
 
   * @return whether the mouse was able to run.
 
   */
 
   */
Line 430: Line 437:
 
   }
 
   }
 
}
 
}
</java5>
+
</source>
 +
}}
  
== Conceito de Pássaro: Bird ==
+
== Conceito de Pássaro ==
  
<java5>
+
{{CollapsedCode|Ficheiro '''Bird.java'''|
 +
<source lang="java">
 
public class Bird {
 
public class Bird {
  
Line 459: Line 468:
 
   * When a bird runs, the energy decreases by 5 units. This value could be
 
   * When a bird runs, the energy decreases by 5 units. This value could be
 
   * defined as an attribute or as a constant.
 
   * defined as an attribute or as a constant.
   *  
+
   *
 
   * @return whether the bird was able to run.
 
   * @return whether the bird was able to run.
 
   */
 
   */
Line 472: Line 481:
 
   * When a bird flies, the energy decreases by 2 units. This value could be
 
   * When a bird flies, the energy decreases by 2 units. This value could be
 
   * defined as an attribute or as a constant.
 
   * defined as an attribute or as a constant.
   *  
+
   *
 
   * @return whether the bird was able to fly.
 
   * @return whether the bird was able to fly.
 
   */
 
   */
Line 525: Line 534:
 
   }
 
   }
 
}
 
}
</java5>
+
</source>
 +
}}
  
 
== Programa Principal ==
 
== Programa Principal ==
  
<java5>
+
Este programa implementa o cenário descrito no enunciado do problema.
 +
 
 +
{{CollapsedCode|Ficheiro '''Application.java'''|
 +
<source lang="java">
 
public class Application {
 
public class Application {
  
/**
+
    /**
* The application scenario.
+
    * @param args
*
+
    */
* @param args
+
    public static void main(String[] args) {
*/
+
        Dog d1 = new Dog("Piloto");
@SuppressWarnings("nls")
+
        Dog d2 = new Dog("Átila");
public static void main(String[] args) {
 
Dog d1 = new Dog("Piloto");
 
Dog d2 = new Dog("Átila");
 
  
Cat c1 = new Cat("Tareco");
+
        Cat c1 = new Cat("Tareco");
Cat c2 = new Cat("Pantufa");
+
        Cat c2 = new Cat("Pantufa");
Cat c3 = new Cat("Kitty");
+
        Cat c3 = new Cat("Kitty");
+
       
Bird[] birds = new Bird[20];
+
        Bird[] birds = new Bird[20];
for (int ix = 0; ix < birds.length; ix++)
+
        for (int ix = 0; ix < birds.length; ix++)
birds[ix] = new Bird();
+
            birds[ix] = new Bird();
  
Mouse[] mice = new Mouse[50];
+
        Mouse[] mice = new Mouse[50];
for (int ix = 0; ix < mice.length; ix++)
+
        for (int ix = 0; ix < mice.length; ix++)
mice[ix] = new Mouse();
+
            mice[ix] = new Mouse();
  
// snapshot: present everything
+
        // snapshot: present everything
System.out.println("BEFORE");
+
        System.out.println("BEFORE");
System.out.println(d1);
+
        System.out.println(d1);
System.out.println(d2);
+
        System.out.println(d2);
System.out.println(c1);
+
        System.out.println(c1);
System.out.println(c2);
+
        System.out.println(c2);
System.out.println(c3);
+
        System.out.println(c3);
  
for (int ix = 0; ix < birds.length; ix++)
+
        for (int ix = 0; ix < birds.length; ix++)
System.out.println(birds[ix]);
+
            System.out.println(birds[ix]);
  
for (int ix = 0; ix < mice.length; ix++)
+
        for (int ix = 0; ix < mice.length; ix++)
System.out.println(mice[ix]);
+
            System.out.println(mice[ix]);
  
// run, chase, eat, sleep, etc.
+
        // run, chase, eat, sleep, etc.
  
for (int ix = 0; ix < birds.length; ix++)
+
        for (int ix = 0; ix < birds.length; ix++)
birds[ix].fly();
+
            birds[ix].fly();
  
d1.run();
+
        d1.run();
d2.attackCat(c1);
+
        d2.attackCat(c1);
c2.eatBird(birds[2]);
+
        c2.eatBird(birds[2]);
c3.eatBird(birds[9]);
+
        c3.eatBird(birds[9]);
c3.eatMouse(mice[0]);
+
        c3.eatMouse(mice[0]);
d2.eatMouse(mice[1]);
+
        d2.eatMouse(mice[1]);
mice[3].run();
+
        mice[3].run();
  
// snapshot: present everything
+
        // snapshot: present everything
System.out.println("AFTER");
+
        System.out.println("AFTER");
System.out.println(d1);
+
        System.out.println(d1);
System.out.println(d2);
+
        System.out.println(d2);
System.out.println(c1);
+
        System.out.println(c1);
System.out.println(c2);
+
        System.out.println(c2);
System.out.println(c3);
+
        System.out.println(c3);
  
for (int ix = 0; ix < birds.length; ix++)
+
        for (int ix = 0; ix < birds.length; ix++)
System.out.println(birds[ix]);
+
            System.out.println(birds[ix]);
  
for (int ix = 0; ix < mice.length; ix++)
+
        for (int ix = 0; ix < mice.length; ix++)
System.out.println(mice[ix]);
+
            System.out.println(mice[ix]);
  
}
+
    }
 
}
 
}
</java5>
+
</source>
 +
}}
  
= Compiling and Running =
+
= Compilação e Execução =
  
== How to Compile? ==
+
== Como compilar? ==
The compilation is as follows:
+
A compilação processa-se como indicado (poder-se-ia compilar ficheiro a ficheiro):
  
   javac Dog.java
+
   javac Dog.java Cat.java Mouse.java Bird.java
  javac Cat.java
 
  javac Mouse.java
 
  javac Bird.java
 
 
   javac Application.java
 
   javac Application.java
  
In fact, compiling Application.java would cause the rest of them be compiled as well (the Java compiler accounts for all explicit class dependencies).
+
De facto, a compilação do ficheiro '''Application.java''' seria suficiente para causar a compilação de todos os outros, já que são referidos a partir dele (o compilador de Java é capaz de seguir dependências explícitas entre classes).
 +
 
 +
== Execução ==
  
== Running ==
+
O programa começa a sua execução na função '''main''' da classe escolhida para iniciar a aplicação (neste caso, '''Application'''):
The program starts at a '''main''' function (in this case, contained in the '''Application''' class):
 
  
 
   java Application
 
   java Application
  
[[category:Teaching]]
 
 
[[category:PO]]
 
[[category:PO]]
 +
[[category:PO Exemplos]]
 +
[[category:Java]]
 +
[[category:Ensino]]

Latest revision as of 01:30, 15 September 2023

Problema

Numa casa de campo existem vários animais.

Alguns animais são domésticos: cães, gatos e pássaros (canários, etc.). Os donos acreditam em dar liberdade completa aos animais, o que causa alguns problemas de interacção nem sempre do agrado geral.

Outros animais, embora vivam na casa ou perto dela, não são oficialmente considerados animais domésticos: ratos, cobras, insectos, aranhas, etc. Estes animais também se deslocam pela propriedade, livre mas nem sempre impunemente.

Todos os animais podem correr (e os pássaros voar), consumindo energia para o efeito. Quando a energia termina, não podem correr mais e têm de dormir para recuperar forças.

Além do repouso, os cães e os gatos podem recuperar energia comendo ratos. Um rato devorado perde, claro está, toda a energia (é transferida para o predador). Os gatos, por serem ágeis, também conseguem comer pássaros (com efeitos muito semelhantes aos da relação gato-rato).

Por vezes, os cães perdem a paciência e atacam os gatos. Ambos perdem energia no processo.

Modele os conceitos "cão", "gato", "pássaro" e "rato". Além da energia, os cães e os gatos têm nome (uma cadeia de caracteres).

Considere que a energia disponível inicialmente para os cães, gatos, pássaros e ratos é, respectivamente, de 1000, 500, 20 e 50 unidades. Quando os animais correm, gastam, respectivamente, 50, 25, 5 e 2 unidades. Um pássaro, quando voa, gasta apenas 2 unidades. Um cão que ataque um gato gasta 100 unidades e faz com que o gato perca 25.

Para um predador comer uma presa tem de a perseguir para a capturar (podendo a perseguição ser ou não bem sucedida). Um cão consegue capturar um rato em cada 25 tentativas. Para os gatos, o rácio é 1 em 5 (ratos) e 1 em 10 (pássaros). A perseguição consome a mesma energia que correr (para cada interveniente), mas a presa recebe um bónus de 5 unidades se escapar.

Construa uma aplicação onde existem 2 cães ("Piloto" e "Átila"), 3 gatos ("Tareco", "Pantufa" e "Kitty"), 20 pássaros e 50 ratos (os pássaros e ratos podem ser organizados em arrays).

Neste cenário, os gatos correm, perseguem pássaros e ratos e são atacados pelos cães, que também podem correr e perseguir e comer ratos. Os animais dormem automaticamente se ficam sem energia (excepto quando são devorados: nesse caso devem ser considerados mortos).

Apresente o estado inicial dos animais (métodos toString) e o estado final (depois de algumas interacções).

(Pode utilizar parte do resultado do Exercício 01 na resolução deste exercicio.)

Solução

A solução apresentada procura manter-se simples e a um nível que ainda não implica utilizar abstracção de propriedade comuns, como por exemplo, a energia, ou de conceitos mais básicos (o de Animal, por exemplo). Estes aspectos, necessários numa boa definição de solução, são objecto de estudo em fases mais adiantadas da exposição da matéria.

UML: Diagrama de Classes

Neste caso, como se escolheu (dada a fase introdutória) não representar os conceitos hierarquizados, apenas existem relações de dependência entre as classes (representadas pelas setas a tracejado).

Existem opções de desenho sub-óptimas. Consegue detectá-las?

BUG: o método "escaped" deveria ter como tipo de retorno "void" e não "boolean".

Diagrama de classes

PO-dog-cat-mouse-bird-energy-noinheritance.png

Conceito de Cão

Ficheiro Dog.java
public class Dog {

    /**
     * We define the base energy as a constant, but it does not have to be this
     * way. It could be defined differently for each dog (but this requirement
     * does not exist in this case).
     */
    private static final int BASE_ENERGY = 1000;

    /**
     * The dog's name.
     */
    private String _name;

    /**
     * The dog's current energy value.
     */
    private int _energy = BASE_ENERGY;

    /**
     * Initialize a dog with a name. Default energy levels are used.
     *
     * @param name
     */
    public Dog(String name) {
        _name = name;
    }

    /**
     * @return dog's current energy level.
     */
    public int getEnergy() {
        return _energy;
    }

    /**
     * @return dog's name
     */
    public String getName() {
        return _name;
    }

    /**
     * Set the dog's name
     *
     * @param name
     *            the dog's name
     */
    public void setName(String name) {
        _name = name;
    }

    /**
     * When a dog runs, the energy decreases by 50 units. This value could be
     * defined as an attribute or as a constant.
     *
     * @return whether the dog was able to run.
     */
    public boolean run() {
        if (_energy < 50)
            return false;
        _energy -= 50;
        return true;
    }

    /**
     * Call "run" to account for spent energy.
     *
     * @param mouse
     *            the mouse to be chased.
     * @return whether the dog was able to catch the mouse. If the mouse
     *         escapes, its energy increases.
     */
    public boolean caughtMouse(Mouse mouse) {
        run();
        mouse.run();
        if (0 == (int) (25 * Math.random())) {
            return true;
        }
        mouse.escaped();
        return false;
    }

    /**
     * Eating is more or less like a vampire feeding...
     *
     * @param mouse
     *            the mouse to eat.
     */
    public void eatMouse(Mouse mouse) {
        if (caughtMouse(mouse))
            _energy += mouse.drain();
    }

    /**
     * We assume that the dog is always able to attack the cat. The parameter to
     * ''attacked'' is used to specify the amount of energy lost by the cat.
     * Note that we are assuming that the degree of loss depends on the attacker
     * (hence the value being defined in the dog class).
     *
     * The energy values could be defined as attributes or as constants.
     *
     * @param cat
     *            the cat the dog attacks
     */
    public void attackCat(Cat cat) {
        _energy -= 100;
        cat.attacked(25);
    }

    /**
     * Energy is recovered when sleeping.
     */
    public void sleep() {
        _energy = BASE_ENERGY;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof Dog) {
            Dog dog = (Dog) o;
            return _name.equals(dog.getName()) && _energy == dog.getEnergy();
        }
        return false;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return _name + " (dog) (" + _energy + ")";
    }
}

Conceito de Gato

Ficheiro Cat.java
public class Cat {

    /**
     * We define the base energy as a constant, but it does not have to be this
     * way. It could be defined differently for each dog (but this requirement
     * does not exist in this case).
     */
    private static final int BASE_ENERGY = 500;

    /**
     * The cat's name.
     */
    private String _name;

    /**
     * The cat's current energy value.
     */
    private int _energy = BASE_ENERGY;

    /**
     * Initialize a cat with a name. Default energy levels are used.
     *
     * @param name
     */
    public Cat(String name) {
        _name = name;
    }

    /**
     * @return cat's current energy level.
     */
    public int getEnergy() {
        return _energy;
    }

    /**
     * @return cat's name
     */
    public String getName() {
        return _name;
    }

    /**
     * Set the cat's name
     *
     * @param name
     *            the cat's name
     */
    public void setName(String name) {
        _name = name;
    }

    /**
     * When a cat runs, the energy decreases by 25 units. This value could be
     * defined as an attribute or as a constant.
     *
     * @return whether the cat was able to run.
     */
    public boolean run() {
        if (_energy < 25)
            return false;
        _energy -= 25;
        return true;
    }

    /**
     * Call "run" to account for spent energy.
     *
     * @param mouse
     *            the mouse to be caught.
     * @return whether the cat was able to catch the mouse. If the mouse
     *         escapes, its energy increases.
     */
    public boolean caughtMouse(Mouse mouse) {
        run();
        mouse.run();
        if (0 == (int) (5 * Math.random())) {
            return true;
        }
        mouse.escaped();
        return false;
    }

    /**
     * Call "run" ("fly"??) to account for spent energy.
     *
     * @param bird
     *            the bird to be caught.
     * @return whether the cat was able to catch the bird. If the bird escapes,
     *         its energy increases.
     */
    public boolean caughtBird(Bird bird) {
        run();
        bird.fly(); // run??
        if (0 == (int) (10 * Math.random())) {
            return true;
        }
        bird.escaped();
        return false;
    }

    /**
     * Eating is more or less like a vampire feeding...
     *
     * @param mouse
     *            the mouse to eat.
     */
    public void eatMouse(Mouse mouse) {
        if (caughtMouse(mouse))
            _energy += mouse.drain();
    }

    /**
     * Eating is more or less like a vampire feeding...
     *
     * @param bird
     *            the mouse to eat.
     */
    public void eatBird(Bird bird) {
        if (caughtBird(bird))
            _energy += bird.drain();
    }

    /**
     * We should probably check for large decrease values. Nevertheless, in this
     * case, for simplicity, we will let the energy go negative and, later on,
     * the cat can recover after a nap.
     *
     * @param energyDecrease
     */
    public void attacked(int energyDecrease) {
        _energy -= energyDecrease;
    }

    /**
     * Energy is recovered when sleeping.
     */
    public void sleep() {
        _energy = BASE_ENERGY;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof Cat) {
            Cat cat = (Cat) o;
            return _name.equals(cat.getName()) && _energy == cat.getEnergy();
        }
        return false;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return _name + " (cat) (" + _energy + ")";
    }
}

Conceito de Rato

Ficheiro Mouse.java
public class Mouse {

  /**
   * We define the base energy as a constant, but it does not have to be this
   * way. It could be defined differently for each dog (but this requirement
   * does not exist in this case).
   */
  private static final int BASE_ENERGY = 50;

  /**
   * The mouse's current energy value.
   */
  private int _energy = BASE_ENERGY;

  /**
   * @return mouse's current energy level.
   */
  public int getEnergy() {
    return _energy;
  }

  /**
   * When a mouse runs, the energy decreases by 2 units. This value could be
   * defined as an attribute or as a constant.
   *
   * @return whether the mouse was able to run.
   */
  public boolean run() {
    if (_energy < 2)
      return false;
    _energy -= 2;
    return true;
  }

  /**
   * Energy goes up 5 points in a narrow escape.
   */
  public void escaped() {
    _energy += 5;
  }

  /**
   * @return the energy level in this mouse
   */
  public int drain() {
    int energy = _energy;
    _energy = 0;
    return energy;
  }

  /**
   * Energy is recovered when sleeping.
   */
  public void sleep() {
    _energy = BASE_ENERGY;
  }

  /**
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object o) {
    if (o instanceof Mouse) {
      Mouse mouse = (Mouse) o;
      return _energy == mouse.getEnergy();
    }
    return false;
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return "mouse (" + _energy + ")";
  }
}

Conceito de Pássaro

Ficheiro Bird.java
public class Bird {

  /**
   * We define the base energy as a constant, but it does not have to be this
   * way. It could be defined differently for each dog (but this requirement
   * does not exist in this case).
   */
  private static final int BASE_ENERGY = 20;

  /**
   * The bird's current energy value.
   */
  private int _energy = BASE_ENERGY;

  /**
   * @return mouse's current energy level.
   */
  public int getEnergy() {
    return _energy;
  }

  /**
   * When a bird runs, the energy decreases by 5 units. This value could be
   * defined as an attribute or as a constant.
   *
   * @return whether the bird was able to run.
   */
  public boolean run() {
    if (_energy < 5)
      return false;
    _energy -= 5;
    return true;
  }

  /**
   * When a bird flies, the energy decreases by 2 units. This value could be
   * defined as an attribute or as a constant.
   *
   * @return whether the bird was able to fly.
   */
  public boolean fly() {
    if (_energy < 2)
      return false;
    _energy -= 2;
    return true;
  }

  /**
   * Energy goes up 5 points in a narrow escape.
   */
  public void escaped() {
    _energy += 5;
  }

  /**
   * @return the energy level in this mouse
   */
  public int drain() {
    int energy = _energy;
    _energy = 0;
    return energy;
  }

  /**
   * Energy is recovered when sleeping.
   */
  public void sleep() {
    _energy = BASE_ENERGY;
  }

  /**
   * @see java.lang.Object#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object o) {
    if (o instanceof Bird) {
      Bird bird = (Bird) o;
      return _energy == bird.getEnergy();
    }
    return false;
  }

  /**
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return "bird (" + _energy + ")";
  }
}

Programa Principal

Este programa implementa o cenário descrito no enunciado do problema.

Ficheiro Application.java
public class Application {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Dog d1 = new Dog("Piloto");
        Dog d2 = new Dog("Átila");

        Cat c1 = new Cat("Tareco");
        Cat c2 = new Cat("Pantufa");
        Cat c3 = new Cat("Kitty");
        
        Bird[] birds = new Bird[20];
        for (int ix = 0; ix < birds.length; ix++)
            birds[ix] = new Bird();

        Mouse[] mice = new Mouse[50];
        for (int ix = 0; ix < mice.length; ix++)
            mice[ix] = new Mouse();

        // snapshot: present everything
        System.out.println("BEFORE");
        System.out.println(d1);
        System.out.println(d2);
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);

        for (int ix = 0; ix < birds.length; ix++)
            System.out.println(birds[ix]);

        for (int ix = 0; ix < mice.length; ix++)
            System.out.println(mice[ix]);

        // run, chase, eat, sleep, etc.

        for (int ix = 0; ix < birds.length; ix++)
            birds[ix].fly();

        d1.run();
        d2.attackCat(c1);
        c2.eatBird(birds[2]);
        c3.eatBird(birds[9]);
        c3.eatMouse(mice[0]);
        d2.eatMouse(mice[1]);
        mice[3].run();

        // snapshot: present everything
        System.out.println("AFTER");
        System.out.println(d1);
        System.out.println(d2);
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);

        for (int ix = 0; ix < birds.length; ix++)
            System.out.println(birds[ix]);

        for (int ix = 0; ix < mice.length; ix++)
            System.out.println(mice[ix]);

    }
}

Compilação e Execução

Como compilar?

A compilação processa-se como indicado (poder-se-ia compilar ficheiro a ficheiro):

 javac Dog.java Cat.java Mouse.java Bird.java
 javac Application.java

De facto, a compilação do ficheiro Application.java seria suficiente para causar a compilação de todos os outros, já que são referidos a partir dele (o compilador de Java é capaz de seguir dependências explícitas entre classes).

Execução

O programa começa a sua execução na função main da classe escolhida para iniciar a aplicação (neste caso, Application):

 java Application