Herança e Composição/Exercício 04: Arabian Nights in Java
From Wiki**3
Problema
Aplicando os conceitos OO que já conhece, concretize as classes necessárias para representar a funcionalidade que se descreve de seguida. Pode ser necessária a criação de classes adicionais não descritas abaixo. A correcta execução dos testes depende da estrita aderência à nomenclatura das classes apresentadas. Todas as classes indicadas devem pertencer à "package" arabiannights.
Funcionalidade da lâmpada mágica (classe MagicLamp)
Uma lâmpada mágica liberta génios quando esfregada (método rub). Os génios podem ser bem ou mal-humorados. O humor dos génios é determinado pelas condições da lâmpada: sempre que a lâmpada tiver sido esfregada um número par de vezes (sem contar a actual), o génio sai mal-humorado. A quantidade de génios disponíveis é determinada no momento de encantamento da lâmpada (criação). Depois de esgotados os génios disponíveis, já não adianta esfregar a lâmpada para obter um génio, bem ou mal-humorado: nestas condições, a lâmpada cria um pequeno demónio que responde a pedidos de forma literal mas perversa. Devido a requisitos de sustentabilidade ambiental, as normas de produção exigem que as lâmpadas sejam recarregáveis. Assim, é possível voltar a obter génios quando se esfrega a lâmpada (em número igual ao inicial). O processo de recarregamento exige apenas que um demónio seja alimentado à lâmpada (método feedDemon).
Quando se cria uma nova lâmpada é necessário indicar a quantidade inicial de génios que é possível invocar. É possível saber quantos génios ainda estão disponíveis na lâmpada (método getGenies ). É ainda possível saber quantas vezes a lâmpada já foi recarregada (método getDemons ). Quando se esfrega a lâmpada, deve-se indicar quantos desejos se espera que o génio realize (independentemente de o génio os poder negar).
A classe MagicLamp deve saber comparar as suas instâncias (através do método equals). Considera-se que duas lâmpadas são iguais se tiverem a mesma capacidade e se os valores retornados pelos métodos getGenies e getDemons forem iguais.
Nota: a lâmpada liberta apenas um génio de cada vez.
Funcionalidade do génio bem-humorado (classe FriendlyGenie)
O génio bem-humorado concede todos os desejos que lhe forem colocados (método grantWish e retorno true), até ao limite com que foi chamado da lâmpada. Depois do limite já não são concedidos desejos (retorno false). É possível saber quantos desejos já foram concedidos (método getGrantedWishes ) e quantos ainda existem disponíveis (método getRemainingWishes).
Nota: o génio concede apenas um desejo de cada vez.
Funcionalidade do génio mal-humorado (classe GrumpyGenie)
O génio mal-humorado concede apenas o primeiro desejo que lhe for colocado (método grantWish e retorno true), independentemente do limite com que foi chamado da lâmpada (retorno false após o primeiro). É possível saber se o desejo já foi realizado (método getGrantedWishes retorna 1).
Funcionalidade do demónio reciclável (classe RecyclableDemon)
O demónio concede todos os desejos que lhe forem colocados (método grantWish e retorno true), independentemente do limite com que foi chamado da lâmpada. Se o demónio for recolocado na lâmpada (para a recarregar), já não pode realizar mais desejos (retorno false). É possível saber quantos desejos já foram concedidos (método getGrantedWishes).
Nota: o demónio concede apenas um desejo de cada vez.
Observações
Todas as classes devem ter métodos de acesso (get e set) (quando apropriado) para os seus atributos.
O método toString, aplicado aos génios e ao demónio, deve devolver uma das seguintes cadeias de caracteres:
- Friendly genie has granted # wishes and still has # to grant. (# representam os contadores apropriados)
- Grumpy genie has granted a wish. / Grumpy genie has a wish to grant. (consoante já concedeu ou não o pedido)
- Recyclable demon has granted # wishes. / Demon has been recycled. (antes e depois de recarregar uma lâmpada)
A classe cliente, de nome ArabianNights deve possuir um método (static) main que execute a seguinte sequência de operações:
- Criar uma lâmpada mágica com capacidade para 4 génios.
- Esfregar 5 vezes a lâmpada, indicando os números de desejos 2, 3, 4, 5, 1.
- Invocar e imprimir o resultado do método toString sobre cada um dos génios.
- Pedir um desejo a cada um dos génios.
- Invocar e imprimir o resultado do método toString sobre cada um dos génios.
- Pedir um desejo a cada um dos génios.
- Invocar e imprimir o resultado do método toString sobre cada um dos génios.
- Colocar o demónio reciclável na lâmpada.
- Esfregar a lâmpada, indicando 7 como número de desejos.
- Invocar e imprimir o resultado do método toString sobre o génio obtido.
Solution
The following corresponds to a possible telling of a special version of the Arabian Nights stories. In any implementation, the following concepts will exist in one form or another: MagicLamp, Genie (a general type of genie, which may also describe the recyclable demon), FriendlyGenie, GrumpyGenie, and RecyclableDemon.
UML Class Diagram
| Diagrama de classes |
|---|
Class MagicLamp
| 'Ficheiro MagicLamp.java' |
|---|
| {{{2}}} |
Class Genie
Class Genie defines the basic behaviour for all genies.
Note that, since Genie is a class whose methods are to be redefined by subclasses and whose references will only point to subclass instances.
| 'Ficheiro Genie.java' |
|---|
| {{{2}}} |
Class FriendlyGenie
A friendly genie is almost like a general genie (it differs in the output messages).
<java5> package arabiannights;
/**
* A friendly genie is actually a normal genie, but with special descriptive text. */
public class FriendlyGenie extends Genie {
/** * @param limit * the maximum number of wishes to grant. */ public FriendlyGenie(int limit) { super(limit); }
/** * @return the number of wishes left to grant. */ public int getRemainingWishes() { return getLimit() - getGrantedWishes(); }
/** * @see java.lang.Object#toString() */ @Override public String toString() { return "Friendly genie has granted " + getGrantedWishes() + " wishes and still has " + getRemainingWishes() + " to grant."; }
} </java5>
Class GrumpyGenie
Redefines a few methods.
<java5> package arabiannights;
/**
* Behaves like any other genie, but with some differences (only one wish). */
public class GrumpyGenie extends Genie {
/** * @param limit * the number of wishes to grant (ignored: always grants only one wish). */ public GrumpyGenie(int limit) { super(1); }
/** * @see java.lang.Object#toString() */ @Override public String toString() { return (getGrantedWishes() == 1) ? "Grumpy genie has granted a wish." : "Grumpy genie has a wish to grant."; }
} </java5>
Class RecyclableDemon
The RecyclableDemon is a genie that implements some of the control methods in a special way (but still adheres to the template method defined by class Genie).
<java5> package arabiannights;
/**
* Recyclable demon. */
public class RecyclableDemon extends Genie {
/** Has been recycled? */ private boolean _recycled = false;
/** * @param limit * the number of wishes to grant (ignored: always grants wishes). */ public RecyclableDemon(int limit) { super(limit); }
/** * Recyclable demons always grant wishes (unless they have been recycled). * * @return true if not yet recycled; false, otherwise. */ @Override public boolean canGrantWish() { return !_recycled; }
/** * @return whether a demon has been recycled. */ public boolean recycled() { return _recycled; }
/** * Recycle a demon (called by a magic lamp). */ public void recycle() { _recycled = true; }
/** * @see java.lang.Object#toString() */ @Override public String toString() { return _recycled ? "Demon has been recycled." : "Recyclable demon has granted " + getGrantedWishes() + " wishes."; }
} </java5>
The Application
In this case, the main class is straightforward.
Numbers in comments correspond to the specified steps in the problem above.
| 'Ficheiro ArabianNights.java' |
|---|
| {{{2}}} |
Compiling and Running
How to Compile?
The compilation is as follows:
javac arabiannights/MagicLamp.java javac arabiannights/Genie.java javac arabiannights/FriendlyGenie.java javac arabiannights/GrumpyGenie.java javac arabiannights/RecyclableDemon.java javac ArabianNights.java
In fact, compiling ArabianNights.java would cause the rest of them be compiled as well (the Java compiler accounts for all explicit class dependencies).
We assume that the CLASSPATH environment variable contains the current directory
Running
The program starts at a main function (in this case, contained in the ArabianNights class):
java ArabianNights
