|
|
(One intermediate revision by the same user not shown) |
Line 1: |
Line 1: |
| {{TOCright}}
| | #REDIRECT [[ist:Herança e Composição/Exercício 03: Energia]] |
| Considere o seguinte problema (baseado no domínio do [[Introdução aos Objectos/Exercício 02: Energia|exercício 02]]). Considere ainda as possíveis abstracções de comportamentos e propriedades comuns aos vários conceitos. Defina os conceitos do problema com base nas suas abstracções.
| |
| | |
| == 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. 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).
| |
| | |
| 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).
| |
| | |
| Discuta as opções da abstracção, em particular, no que respeita a aspectos menos flexíveis relativamente a possíveis alterações do modelo (constantes, propriedades, comportamentos comuns, etc.). Ainda neste sentido, compare a nova solução com a [[Introdução aos Objectos/Exercício 02: Energia|solução da anterior]]. Chegou à conclusão que a sua nova solução ainda apresenta dificuldades face à manutenção do código? Neste caso, quais são os aspectos problemáticos?
| |
| | |
| = Solução =
| |
| | |
| A solução apresentada procura uma abstracção adequada das propriedades e comportamentos intrínsecos dos conceitos em causa. Alguns aspectos, relativos à abstracção funcional não são ainda considerados.
| |
| | |
| == UML: Diagrama de Classes ==
| |
| | |
| {{CollapsedCode|Diagrama de classes|
| |
| [[Image:PO-dog-cat-mouse-bird-energy-with-inheritance.png]]
| |
| }}
| |
| | |
| == Conceito de Animal ==
| |
| | |
| Conceito que gere a energia e o consumo quando se corre, i.e., todos os animais têm energia (boa abstracção) e todos correm (não tão boa, mas neste caso é aceitável, embora haja melhores soluções utilizando técnicas mais avançadas de modelação).
| |
| | |
| {{CollapsedCode|Ficheiro '''Animal.java'''|
| |
| <java5>
| |
| public class Animal {
| |
| | |
| /** The animal's base energy value. */
| |
| private final int _baseEnergy;
| |
| | |
| /** The dog'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
| |
| */
| |
| 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) {
| |
| this(baseEnergy, baseEnergy, runEnergy);
| |
| }
| |
| | |
| /**
| |
| * @return the animal's base energy level.
| |
| */
| |
| public int getBaseEnergy() {
| |
| return _energy;
| |
| }
| |
| | |
| /**
| |
| * @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
| |
| */
| |
| void increaseEnergy(int delta) {
| |
| _energy += delta;
| |
| }
| |
| | |
| /**
| |
| * When an animal runs, the energy decreases.
| |
| *
| |
| * @return whether the dog was able to run.
| |
| */
| |
| public boolean run() {
| |
| if (_energy < _runEnergy)
| |
| return false;
| |
| _energy -= _runEnergy;
| |
| return true;
| |
| }
| |
| | |
| /**
| |
| * 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;
| |
| }
| |
| }
| |
| </java5>
| |
| }}
| |
| | |
| == Conceito de Animal com Nome ==
| |
| | |
| Tal como no caso anterior, utilizando técnicas de modelação mais avançadas, este conceito poderia não ser sobrecarregado com alguns aspectos, presentes apenas por serem comuns às subclasses (nomeadamente, perseguir e comer ratos).
| |
| | |
| {{CollapsedCode|Ficheiro '''NamedAnimal.java'''|
| |
| <java5>
| |
| public class NamedAnimal extends Animal {
| |
| | |
| /**
| |
| * The animal's name.
| |
| */
| |
| private String _name;
| |
| | |
| /**
| |
| * Catch rate: success in catching mice.
| |
| */
| |
| private int _catchRate;
| |
|
| |
| /**
| |
| * Note that baseEnergy and runEnergy are final and thus, constant after
| |
| * initialization.
| |
| *
| |
| * @param name
| |
| * @param catchRate
| |
| * @param baseEnergy
| |
| * @param energy
| |
| * @param runEnergy
| |
| */
| |
| NamedAnimal(String name, int catchRate, int baseEnergy, int energy, int runEnergy) {
| |
| super(baseEnergy, energy, runEnergy);
| |
| _name = name;
| |
| _catchRate = catchRate;
| |
| }
| |
| | |
| /**
| |
| * @param name
| |
| * @param catchRate
| |
| * @param baseEnergy
| |
| * @param runEnergy
| |
| */
| |
| NamedAnimal(String name, int catchRate, int baseEnergy, int runEnergy) {
| |
| super(baseEnergy, runEnergy);
| |
| _name = name;
| |
| _catchRate = catchRate;
| |
| }
| |
| | |
| /**
| |
| * @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;
| |
| }
| |
| | |
| /**
| |
| * This is not really a good place for this method, but since only named
| |
| * animals catch mice, we will leave it here for the time being.
| |
| *
| |
| * 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) (_catchRate * Math.random())) {
| |
| return true;
| |
| }
| |
| mouse.escaped();
| |
| return false;
| |
| }
| |
| | |
| /**
| |
| * This is not really a good place for this method, but since only named
| |
| * animals eat mice, we will leave it here for the time being.
| |
| *
| |
| * Eating is more or less like a vampire feeding...
| |
| *
| |
| * @param mouse
| |
| * the mouse to eat.
| |
| */
| |
| public void eatMouse(Mouse mouse) {
| |
| if (caughtMouse(mouse))
| |
| increaseEnergy(mouse.drain());
| |
| }
| |
| | |
| /**
| |
| * @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();
| |
| }
| |
| }
| |
| </java5>
| |
| }}
| |
| | |
| == Conceito de Cão ==
| |
| | |
| Um cão é um animal com nome, com uma dada energia inicial e que consome uma determinada energia quando corre (ver definição destes parâmetros na chamada ao construtor).
| |
| | |
| Comparar a reduzida dimensão da classe, quando comparada com a versão sem herança.
| |
| | |
| {{CollapsedCode|Ficheiro '''Dog.java'''|
| |
| <java5>
| |
| public class Dog extends NamedAnimal {
| |
| | |
| /**
| |
| * Base energy level: 500. Run spending: 25.
| |
| *
| |
| * @param name
| |
| */
| |
| 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 java.lang.Object#toString()
| |
| */
| |
| @Override
| |
| public String toString() {
| |
| return "Dog: " + super.toString();
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#equals(java.lang.Object)
| |
| */
| |
| @Override
| |
| public boolean equals(Object o) {
| |
| if (o instanceof Dog)
| |
| return super.equals(o);
| |
| return false;
| |
| }
| |
|
| |
| }
| |
| </java5>
| |
| }}
| |
| | |
| == Conceito de Gato ==
| |
| Um gato é um animal com nome, com uma dada energia inicial e que consome uma determinada energia quando corre (ver definição destes parâmetros na chamada ao construtor).
| |
| | |
| Comparar a reduzida dimensão da classe, quando comparada com a versão sem herança.
| |
| | |
| {{CollapsedCode|Ficheiro '''Cat.java'''|
| |
| <java5>
| |
| public class Cat extends NamedAnimal {
| |
| | |
| /**
| |
| * Base energy level: 500. Run spending: 25.
| |
| *
| |
| * @param name
| |
| */
| |
| Cat(String name) {
| |
| super(name, 5, 500, 25);
| |
| }
| |
| | |
| /**
| |
| * 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 bird
| |
| * the mouse to eat.
| |
| */
| |
| public void eatBird(Bird bird) {
| |
| if (caughtBird(bird))
| |
| increaseEnergy(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) {
| |
| increaseEnergy(-energyDecrease);
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#toString()
| |
| */
| |
| @Override
| |
| public String toString() {
| |
| return "Cat: " + super.toString();
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#equals(java.lang.Object)
| |
| */
| |
| @Override
| |
| public boolean equals(Object o) {
| |
| if (o instanceof Cat)
| |
| return super.equals(o);
| |
| return false;
| |
| }
| |
|
| |
| }
| |
| </java5>
| |
| }}
| |
| | |
| == Conceito de Rato ==
| |
| | |
| O conceito de rato poderia ser melhor definido, mas, nesta fase, utiliza-se um grau de abstracção menos conveniente, evitando definir conceitos apenas para evitar repetição de código (uma decisão que, na ausência de outras técnicas, poderia ter sido tomada na outra direcção -- ver, como exemplo, a classe NamedAnimal, que agrega alguma funcionalidade que não lhe pertence intrinsecamente).
| |
| | |
| Comparar a implementação com a versão sem herança e considerar outras possibilidades de abstracção.
| |
| | |
| {{CollapsedCode|Ficheiro '''Mouse.java'''|
| |
| <java5>
| |
| public class Mouse extends Animal {
| |
| | |
| /**
| |
| * Base energy level: 50. Run spending: 2.
| |
| */
| |
| Mouse() {
| |
| super(50, 2);
| |
| }
| |
|
| |
| /**
| |
| * Energy goes up 5 points in a narrow escape.
| |
| */
| |
| public void escaped() {
| |
| increaseEnergy(5);
| |
| }
| |
| | |
| /**
| |
| * @return the energy level in this mouse
| |
| */
| |
| public int drain() {
| |
| int energy = getEnergy();
| |
| setEnergy(0);
| |
| return energy;
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#toString()
| |
| */
| |
| @Override
| |
| public String toString() {
| |
| return "Mouse: " + super.toString();
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#equals(java.lang.Object)
| |
| */
| |
| @Override
| |
| public boolean equals(Object o) {
| |
| if (o instanceof Mouse)
| |
| return super.equals(o);
| |
| return false;
| |
| }
| |
|
| |
| }
| |
| </java5>
| |
| }}
| |
| | |
| == Conceito de Pássaro ==
| |
| | |
| O conceito de pássaro poderia ser melhor definido, mas, nesta fase, utiliza-se um grau de abstracção menos conveniente, evitando definir conceitos apenas para evitar repetição de código (uma decisão que, na ausência de outras técnicas, poderia ter sido tomada na outra direcção -- ver, como exemplo, a classe NamedAnimal, que agrega alguma funcionalidade que não lhe pertence intrinsecamente).
| |
| | |
| Comparar a implementação com a versão sem herança e considerar outras possibilidades de abstracção.
| |
| | |
| {{CollapsedCode|Ficheiro '''Bird.java'''|
| |
| <java5>
| |
| public class Bird extends Animal {
| |
| | |
| /**
| |
| * Base energy level: 20. Run spending: 5.
| |
| */
| |
| 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;
| |
| }
| |
| | |
| /**
| |
| * Energy goes up 5 points in a narrow escape.
| |
| */
| |
| public void escaped() {
| |
| increaseEnergy(5);
| |
| }
| |
| | |
| /**
| |
| * @return the energy level in this bird
| |
| */
| |
| public int drain() {
| |
| int energy = getEnergy();
| |
| setEnergy(0);
| |
| return energy;
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#toString()
| |
| */
| |
| @Override
| |
| public String toString() {
| |
| return "Bird: " + super.toString();
| |
| }
| |
| | |
| /**
| |
| * @see java.lang.Object#equals(java.lang.Object)
| |
| */
| |
| @Override
| |
| public boolean equals(Object o) {
| |
| if (o instanceof Bird)
| |
| return super.equals(o);
| |
| return false;
| |
| }
| |
|
| |
| }
| |
| </java5>
| |
| }}
| |
| | |
| == Programa Principal ==
| |
| | |
| Almost unchanged from the previous versions (only object construction was changed -- it is simpler now).
| |
| | |
| {{CollapsedCode|Ficheiro '''Application.java'''|
| |
| <java5>
| |
| public class Application {
| |
| | |
| /**
| |
| * The application scenario.
| |
| *
| |
| * @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]);
| |
|
| |
| }
| |
| | |
| }
| |
| </java5>
| |
| }}
| |
| | |
| = Compiling and Running =
| |
| | |
| === How to Compile? ===
| |
| The compilation is as follows:
| |
| | |
| javac Animal.java
| |
| javac NamedAnimal.java
| |
| javac Dog.java
| |
| javac Cat.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
| |
| | |
| [[category:Ensino]]
| |
| [[category:PO]]
| |
| [[category:PO Exemplos]]
| |