Polimorfismo, Interfaces, Classes Abstractas/Exercício 01: Energia, Predadores e Presas

From Wiki**3

< Polimorfismo, Interfaces, Classes Abstractas

Problema

Considere o problema introdutório ou a variante ilustrando a utilização de herança.

Considere agora que existem os conceitos Predador e Presa. Um predador é caracterizado por capturar (caught) e devorar (eat) uma presa, consumindo toda a sua energia (drain). Todas as presas são caracterizadas por terem, além da função de consumo de energia, um predicado que indica se escaparam ao predador (escaped). Tanto os predadores como as presas correm (run).

Considere que, dos animais descritos (cão, gato, rato, pássaro), o cão e o gato são predadores e que o rato e o pássaro são presas. Considere, por simplicidade, que um predador consegue interagir com qualquer presa e que as energias envolvidas são as mesmas em todas as interacções predador presa.

Critique o nível de abstracção que obteve.

Solução

UML: Diagrama de Classes

Diagrama de classes

PO-dog-cat-mouse-bird-energy-predator-prey.png

Conceito de Predador

Ficheiro Predator.java
/**
 * Definition of the predator's interface.
 */
public interface Predator {

    /**
     * Predators run.
     */
    void run();

    /**
     * Predators catch prey.
     *
     * @param prey
     *            the prey to be caught.
     * @return whether the predator was able to catch the prey.
     */
    boolean caught(Prey prey);

    /**
     * @param prey the prey to be eaten.
     */
    void eat(Prey prey);

}

Conceito de Presa

Ficheiro Prey.java
/**
 * Definition of the prey's interface.
 */
public interface Prey {

    /**
     * Preys run.
     */
    void run();

    /**
     * Energy goes up for the prey in a narrow escape.
     */
    void escaped();

    /**
     * @return the energy level in this prey
     */
    int drain();

}

Conceito de Animal

Ficheiro Animal.java
/**
 * This is a basic animal with a given amount of energy.
 */
public abstract class Animal {

    /**
     * The animal's base energy value.
     */
    private final int _baseEnergy;

    /**
     * The animals's current energy value.
     */
    private int _energy;

    /**
     * The animal's energy spent when running.
     */
    private final int _runEnergy;

    /**
     * Note that baseEnergy and runEnergy are final and thus, constant after
     * initialization.
     *
     * @param baseEnergy
     * @param energy
     * @param runEnergy
     */
    public Animal(int baseEnergy, int energy, int runEnergy) {
        _energy = Math.min(energy, baseEnergy);
        _baseEnergy = baseEnergy;
        _runEnergy = Math.min(runEnergy, baseEnergy);
    }

    /**
     * @param baseEnergy
     * @param runEnergy
     */
    Animal(int baseEnergy, int runEnergy) {
        _energy = _baseEnergy = baseEnergy;
        _runEnergy = Math.min(runEnergy, baseEnergy);
    }

    /**
     * @return the animal's base energy level.
     */
    public int getBaseEnergy() {
        return _baseEnergy;
    }

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

    /**
     * @param energy
     *            the animal's new energy level.
     */
    public void setEnergy(int energy) {
        _energy = energy;
    }

    /**
     * @return the animal's energy spent when running
     */
    public int getRunEnergy() {
        return _runEnergy;
    }

    /**
     * @param delta
     */
    public void increaseEnergy(int delta) {
        _energy += delta;
    }

    /**
     * When an animal runs, the energy decreases by _runEnergy units.
     */
    public void run() {
        if (_energy < _runEnergy)
            return;
        _energy -= _runEnergy;
    }

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

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof Animal) {
            Animal animal = (Animal) o;
            return _baseEnergy == animal.getBaseEnergy() && _energy == animal.getEnergy()
                    && _runEnergy == animal.getRunEnergy();
        }
        return false;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "base energy: " + _baseEnergy + ", energy left: " + _energy + ", spent running: "
                + _runEnergy;
    }
}

Conceito de Animal com Nome

Ficheiro NamedAnimal.java
/**
 * This is an animal that has a name.
 */
public abstract class NamedAnimal extends Animal {

    /**
     * The animal's name.
     */
    private String _name = "";

    /**
     * Note that baseEnergy and runEnergy are final and thus, constant after
     * initialization.
     *
     * @param name
     * @param baseEnergy
     * @param energy
     * @param runEnergy
     */
    public NamedAnimal(String name, int baseEnergy, int energy, int runEnergy) {
        super(baseEnergy, energy, runEnergy);
        _name = name;
    }

    /**
     * @param name
     * @param baseEnergy
     * @param runEnergy
     */
    public NamedAnimal(String name, int baseEnergy, int runEnergy) {
        super(baseEnergy, runEnergy);
        _name = name;
    }

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

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

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof NamedAnimal) {
            NamedAnimal animal = (NamedAnimal) o;
            return super.equals(o) && _name.equals(animal.getName());
        }
        return false;
    }

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

Conceito de Cão

Ficheiro Dog.java
/**
 * Definition of a predator dog.
 *
 * Note that, like for mice and birds, we could have defined a PredatorAnimal
 * class for factoring the code common to both cats and dogs. We chose not to do
 * it to illustrate how individual classes must implement methods otherwise not
 * provided (compare "run" defined in Animal).
 */
public class Dog extends NamedAnimal implements Predator {

    /**
     * FIXME: the catch rate should not be fixed.
     *
     * The catch rate.
     */
    private final int CATCH_RATE = 10;

    /**
     * Base energy level: 500. Run spending: 25.
     *
     * @param name
     */
    public Dog(String name) {
        super(name, 25, 1000, 50);
    }

    /**
     * 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) {
        increaseEnergy(-100);
        cat.attacked(25);
    }

    /**
     * @see Predator#caught(Prey)
     */
    public boolean caught(Prey prey) {
        run();
        prey.run();
        if (0 == (int) (CATCH_RATE * Math.random())) {
            return true;
        }
        prey.escaped();
        return false;
    }

    /**
     * @see Predator#eat(Prey)
     */
    public void eat(Prey prey) {
        if (caught(prey))
            increaseEnergy(prey.drain());
    }

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

Conceito de Gato

Ficheiro Cat.java
/**
 * Definition of a predator cat.
 *
 * Note that, like for mice and birds, we could have defined a PredatorAnimal
 * class for factoring the code common to both cats and dogs. We chose not to do
 * it to illustrate how individual classes must implement methods otherwise not
 * provided (compare "run" defined in Animal).
 */
public class Cat extends NamedAnimal implements Predator {

    /**
     * The catch rate.
     */
    private final int CATCH_RATE = 5;
    
    /**
     * Base energy level: 500. Run spending: 25.
     *
     * @param name
     */
    public Cat(String name) {
        super(name, 5, 500, 25);
    }

    /**
     * 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) {
        increaseEnergy(-energyDecrease);
    }


    /**
     * @see Predator#caught(Prey)
     */
    public boolean caught(Prey prey) {
        run();
        prey.run();
        if (0 == (int) (CATCH_RATE * Math.random())) {
            return true;
        }
        prey.escaped();
        return false;
    }

    /**
     * @see Predator#eat(Prey)
     */
    public void eat(Prey prey) {
        if (caught(prey))
            increaseEnergy(prey.drain());
    }

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

Conceito de Animal Presa

Ficheiro AnimalPrey.java
/**
 * Note that class AnimalPrey only exists to factor code common to known preys
 * (mice and birds). A prey class may choose to implement these methods
 * directly.
 */
public abstract class AnimalPrey extends Animal implements Prey {

    /**
     * @see Animal#Animal(int, int)
     */
    public AnimalPrey(int baseEnergy, int runEnergy) {
        super(baseEnergy, runEnergy);
    }

    /**
     * @see Animal#Animal(int, int, int)
     */
    public AnimalPrey(int baseEnergy, int energy, int runEnergy) {
        super(baseEnergy, energy, runEnergy);
    }

    /**
     * @see Prey#drain()
     */
    @Override
    public int drain() {
        int energy = getEnergy();
        setEnergy(0);
        return energy;
    }

    /**
     * @see Prey#escaped()
     */
    @Override
    public void escaped() {
        increaseEnergy(5);
    }

}

Conceito de Rato

Ficheiro Mouse.java
/**
 * A mouse is a simple prey.
 *
 * Note that class AnimalPrey only exists to factor code common to known preys
 * (mice and birds). A prey class may choose to implement those methods
 * directly.
 */
public class Mouse extends AnimalPrey {

    /**
     * Base energy level: 50. Run spending: 2.
     */
    public Mouse() {
        super(50, 2);
    }

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

Conceito de Pássaro

Ficheiro Bird.java
/**
 * A bird is a simple prey and is able to fly.
 *
 * Note that class AnimalPrey only exists to factor code common to known preys
 * (mice and birds). A prey class may choose to implement those methods
 * directly.
 */
public class Bird extends AnimalPrey {

    /**
     * Base energy level: 20. Run spending: 5.
     */
    public Bird() {
        super(20, 5);
    }

    /**
     * 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 (getEnergy() < 2)
            return false;
        increaseEnergy(-2);
        return true;
    }

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

Programa Principal

Simples programa exemplo.

Ficheiro Application.java
public class Application {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Predator d1 = new Dog("Piloto");
        Predator d2 = new Dog("Átila");
        
        Predator c1 = new Cat("Tareco");
        Predator c2 = new Cat("Pantufa");
        Predator c3 = new Cat("Kitty");
        
        Prey[] prey = new Prey[70];
        
        int ix;
        for (ix = 0; ix < 30; ix++)
            prey[ix] = new Bird();

        for (; ix < 70; ix++)
            prey[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);
        
        // run, chase, eat, sleep, etc.
        
        d1.run();
        
        //d2.attackCat(c1);            // not possible for predators
        ((Dog)d2).attackCat((Cat)c1);  // but ok for cats and dogs (bad style)
        
        c2.eat(prey[2]);
        c3.eat(prey[9]);
        c3.eat(prey[0]);
        d2.eat(prey[1]);
        prey[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);

    }

}

Compiling and Running

How to Compile?

The compilation is as follows:

 javac Animal.java
 javac NamedAnimal.java
 javac Predator.java
 javac Dog.java
 javac Cat.java
 javac Prey.java
 javac AnimalPrey.java
 javac Mouse.java
 javac Bird.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).

Running

The program starts at a main function (in this case, contained in the Application class):

 java Application

Agradecimentos

  • Ana Cardoso-Cachopo -- pelo feedback sobre alguns bugs e por ter contribuído o rascunho do diagrama de classes.