Suponha que, num jogo, as jogadas que os jogadores fazem podem ter efeitos diferentes, em vez de resultarem sempre na adição dos pontos da jogada à pontuação geral do jogador. O efeito poderá ser, por exemplo, a adição, a subtração ou a multiplicação dos pontos da jogada à/da/pela pontuação do jogador. Poderá ser ainda a substituição da pontuação do jogador pelos pontos dessa jogada ou por zero.
Uma abordagem NÃO satisfatória para podermos controlar o tipo de efeito que as jogadas devem ter em cada execução do programa, passa por usar uma variável cujo valor represente o efeito desejado. Podemos optar por declarar uma variável efeito
, por exemplo, e dar-lhe valores 1, 2, 3, 4 ou 5 para representar cada efeito (1: Adição, 2: Subtração, 3: Multiplicação, 4: Substituição, 5: Nulo). Ou podemos optar por declarar a variável com o tipo char
e usar as iniciais dos nomes que demos aos efeitos – 'A', ‘S’, 'M', 'U' ou 'N' – para representar cada uma delas (com o inconveniente de ter que evitar letras repetidas).
Esta abordagem, em qualquer das suas formas, tem três inconvenientes:
A linguagem Java permite-nos trabalhar com estes tipos de informação duma forma muito mais segura e que promove a legibilidade dos programas.
Os enumerados no Java permitem-nos definir tipos de dados não primitivos em que
- o conjunto de valores possíveis é conhecido à partida e
- tem um número reduzido de elementos.
Definimos agora o enumerado EfeitoJogada
criando um ficheiro de nome EfeitoJogada.java
(tal como fazemos para as classes) onde definimos todos os valores possíveis para este tipo de dados não primitivo:
public enum EfeitoJogada {
ADICAO, SUBTRACAO, MULTIPLICACAO, SUBSTITUICAO, NULO;
}
Repare na palavra reservada enum
que é usada em substituição da palavra class
.
À semelhança de qualquer outro tipo de dados podemos, por exemplo, declarar variáveis deste novo tipo:
xxxxxxxxxx
public class ClienteDeEfeitoJogada {
public static void main(String[] args) {
EfeitoJogada e1 = EfeitoJogada.ADICAO;
EfeitoJogada e2 = EfeitoJogada.SUBSTITUICAO;
EfeitoJogada e3 = EfeitoJogada.NULO;
}
}
Os benefícios que obtemos pelo facto de usarmos o enumerado EfeitoJogada
são:
EfeitoJogada
; se o fizermos, o compilador assinalará um erro.Os valores de um enumerado (neste caso, desde ADICAO
até NULO
) são constantes, mas também são objetos pois são de um tipo não primitivo.
Cada valor do enumerado está associado a um número desde zero até ao número de elementos do enumerado – 1; esse número é o seu número de ordem (neste caso, os números de ordem de ADICAO
e NULO
são, respetivamente, 0 e 4).
Há vários métodos que são acessíveis a todos os enumerados:
int ordinal()
, um método de instância que devolve o número de ordem do objeto sobre o qual é invocado;String name()
, um método de instância que devolve o nome do objeto sobre o qual é invocado;static EfeitoJogada[] values()
, um método de classe que devolve um array contendo todos os valores do enumerado na ordem pela qual foram definidos (no caso do enumerado EfeitoJogada
retorna um array cujo primeiro elemento é o valor ADICAO
, o segundo é MULTIPLICACAO
, etc),A execução das instruções do seguinte método main
:
xpublic class ClienteDeEfeitoJogada {
public static void main(String[] args) {
EfeitoJogada e1 = EfeitoJogada.ADICAO;
EfeitoJogada e2 = EfeitoJogada.SUBSTITUICAO;
System.out.println(e1.name() + " -- " + e1.ordinal());
System.out.println(e2.name() + " -- " + e2.ordinal());
System.out.println("===============");
EfeitoJogada[] todosEfeitos = EfeitoJogada.values();
for(EfeitoJogada est : todosEfeitos){
System.out.println(est.name() + " -- " + est.ordinal());
}
}
}
Escreve as seguintes linhas no standard output:
xxxxxxxxxx
ADICAO -- 0
SUBSTITUICAO -- 3
===============
ADICAO -- 0
SUBTRACAO -- 1
MULTIPLICACAO -- 2
SUBSTITUICAO -- 3
NULO -- 4
Repare, nas últimas linhas, como os nomes dos efeitos aparecem pela mesma ordem com que foram definidos no enumerado.
Para classificarmos cada jogada com o respetivo efeito temos que alterar o método registarPontosJogada
da classe Jogo
e o método registarPontos
da classe Jogador
apresentados no capítulo anterior. É necessário adicionar um parâmetro do tipo EfeitoJogada
a esses métodos para receber o efeito da jogada a registar. De acordo com o valor desse parâmetro, os pontos a registar vão ter o efeito correspondente ao sentido que demos a cada um dos valores do enumerado.
No método registarPontosJogada
da classe Jogo
a mudança no corpo do método é pequena: basta acrescentar o valor do efeito na invocação do método registarPontos
sobre o jogador em questão.
xxxxxxxxxx
/**
* Registar a pontuacao, na jogada atual, de um dado jogador.
* @param nomeJog O nome do jogador.
* @param pontuacao A pontuacao obtida na jogada atual.
* @param efeito O efeito que a jogada vai ter na pontuacao geral do jogador.
* @requires this.estahEmJogo (nomeJog) &&
* pontuacao >= MIN_PONTUACAO_JOGADA &&
* pontuacao <= MAX_PONTUACAO_JOGADA && efeito != null
*/
public void registarPontosJogada (String nomeJog, int pontuacao, EfeitoJogada efeito) {
this.obterJogador(nomeJog).registarPontos(pontuacao,efeito);
}
No método registarPontos
da classe Jogador
, o valor do efeito define a mudança a aplicar à pontuação atual do jogador corrente – usámos uma instrução switch
.
Em relação ao valor máximo numa só jogada, agora fazemos o cálculo da diferença entre a pontuação que o jogador tinha antes da jogada e a que tem após a jogada. É o valor dessa diferença que é comparado com o atual máximo numa só jogada.
xxxxxxxxxx
/**
* Registar uma nova pontuacao, com um dado efeito, para este jogador
* @param pontos Os pontos a acumular ah pontuacao deste jogador
* @param efeito O efeito que a jogada vai ter na pontuacao geral do jogador.
* @requires pontos >= 0 && efeito != null
*/
public void registarPontos (int pontos, EfeitoJogada efeito) {
int anterior = this.pontuacao;
int registar = pontos;
switch (efeito){
case ADICAO: this.pontuacao += registar;
break;
case SUBTRACAO: this.pontuacao -= registar;
break;
case MULTIPLICACAO: this.pontuacao *= registar;
break;
case SUBSTITUICAO: this.pontuacao = registar;
break;
case NULO: this.pontuacao = 0;
break;
}
// se teve um aumento de pontuacao maior que o que obteve ateh agora,
// passa a ser esse o aumento de pontuacao maximo numa unica jogada
int aumento = this.pontuacao - anterior;
if (aumento > maximoJogada) {
maximoJogada = aumento;
}
}
O seguinte método main
está de acordo com a nova versão do método registarPontosJogada
da classe Jogo
. Além de gerar aleatoriamente os valores para as pontuações das sucessivas jogadas, gera também o efeito que cada jogada vai ter.
Faz isso gerando, a cada jogada de cada jogador, um inteiro entre 0 e 4 (o array EfeitoJogada.values()
tem 5 elementos) e usa o efeito que está nessa posição do array EfeitoJogada.values()
como valor para o parâmetro na invocação do método registarPontosJogada
.
xxxxxxxxxx
import java.util.Random;
public class ClienteDeJogo {
public static void main (String [] args) {
Jogo meuJogo = new Jogo(30);
String[] participantes = {"Maria","Pedro"};
for (int i = 0 ; i < participantes.length ; i++) {
meuJogo.juntarJogador(participantes[i]);
}
EfeitoJogada[] todosEfeitos = EfeitoJogada.values();
Random gerador = new Random();
int max = Jogo.MAX_PONTUACAO_JOGADA;
int min = Jogo.MIN_PONTUACAO_JOGADA;
// Quantos efeitos existem?
int maxEfeito = todosEfeitos.length;
// O jogo eh jogado em rondas.
// Em cada ronda jogam todos os jogadores.
while(!meuJogo.terminou()) {
for (int j = 0 ; j < participantes.length ; j++) {
// gera um valor no intervalo [min,max]
int pontuacao = gerador.nextInt(max - min + 1) + min;
// gera um valor no intervalo [0,maxEfeito[ e usa esse valor
// para obter um efeito aleatorio
EfeitoJogada efeito = todosEfeitos[gerador.nextInt(maxEfeito)];
// regista a pontuacao do proximo jogador
meuJogo.registarPontosJogada(participantes[j], pontuacao, efeito);
System.out.println(participantes[j] + " fez jogada de " +
pontuacao + " pontos com efeito " + efeito.name());
}
System.out.println("=========================================");
System.out.println(meuJogo);
}
System.out.println("Acabou o jogo!");
String[] vencedores = meuJogo.vencedores1();
for (int i = 0 ; i < vencedores.length ; i++) {
System.out.println(vencedores[i] + " venceu com " +
meuJogo.pontuacao(vencedores[i]) + " pontos");
}
}
}
O resultado de uma possível execução deste main
:
xxxxxxxxxx
Maria fez jogada de 3 pontos com efeito NULO
Pedro fez jogada de 4 pontos com efeito ADICAO
=========================================
Objetivo do jogo: 30 pontos
Numero de jogadores: 2
Nome: Maria Pontuacao: 0 Maximo numa jogada: 0
Nome: Pedro Pontuacao: 4 Maximo numa jogada: 4
Maria fez jogada de 7 pontos com efeito SUBTRACAO
Pedro fez jogada de 4 pontos com efeito ADICAO
=========================================
Objetivo do jogo: 30 pontos
Numero de jogadores: 2
Nome: Maria Pontuacao: -7 Maximo numa jogada: 0
Nome: Pedro Pontuacao: 8 Maximo numa jogada: 4
Maria fez jogada de 9 pontos com efeito ADICAO
Pedro fez jogada de 10 pontos com efeito SUBSTITUICAO
=========================================
Objetivo do jogo: 30 pontos
Numero de jogadores: 2
Nome: Maria Pontuacao: 2 Maximo numa jogada: 9
Nome: Pedro Pontuacao: 10 Maximo numa jogada: 4
Maria fez jogada de 1 pontos com efeito ADICAO
Pedro fez jogada de 5 pontos com efeito MULTIPLICACAO
=========================================
Objetivo do jogo: 30 pontos
Numero de jogadores: 2
Nome: Maria Pontuacao: 3 Maximo numa jogada: 9
Nome: Pedro Pontuacao: 50 Maximo numa jogada: 40
Acabou o jogo!
Pedro venceu com 50 pontos
Em capítulos mais à frente, quando falarmos em padrões de desenho, veremos que há formas melhores de abordar este assunto.
Anterior: 11.9. Representação UML
Seguinte: 13. Interfaces