Considere o Exercício 02 da Introdução aos Objectos.
Resolva agora o mesmo problema, mas considerando 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.
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 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?
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.
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).
<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) { _energy = _baseEnergy = baseEnergy; _runEnergy = Math.min(runEnergy, baseEnergy); }
/** * @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 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 < _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>
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).
<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 == animal.getName(); } return false; }
/** * @see java.lang.Object#toString() */ @Override public String toString() { return _name + ", " + super.toString(); } } </java5>
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. <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(); } } </java5>
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. <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(); } } </java5>