Tag: Máquina de estados
Programação avançada – capitulo 8 – maquina de estados, exemplo3 (revisitado)
Revisitar o exercício acerca do uso de maquina de estados com a implementação de um jogo de 3×3, onde existem dois jogadores.
A máquina de estados:
é um padrão de programação;
serve para abordar problemas mais complexos;
vai ajudar no fluxo na evolução das aplicações (redirecciona para o estado que vai fazer um determinado processamento);
permite identificar os momentos importantes, na evolução da aplicação/jogo;
Definição dos estados:
está relacionada com a interacção do utilizador;
existem transições entre os estados;
as transições de entrada (métodos) e podem/devem ser condicionados pelo estado do jogo;
as transições de saída (métodos) e podem/devem ser condicionados pelo estado do jogo;
existem então acções do utilizador sobre o jogo, e estas acções resultam em métodos, para haver evolução no jogo;
(identificar as etapas do jogo, que vão ser implementadas, para resolver mini problemas, sub dividir para se simplificar o código que é implementado);
Os estados, são assim:
são representados através de classes;
existe uma classe base, da qual os estados vão derivar (polimorfismo), normalmente do IEstado (através do implements);
e devem ser implementados todos os métodos abstractos que estão definidos no IEstado (usando return para o new “estado”) ou então fica nele próprio (return this).
(2)O IEstado, serve então:
é uma interface, dos estados (e não uma classe);
serve para representar/declarar o que existe, acções possíveis e comuns em todos os estados (através de métodos);
esses métodos são do tipo da interface IEstado;
mas atenção que podem existir algumas acções que podem não ser usadas/implementadas em todos os estados (fica o return this, nas bases), mas têm que ser aqui colocadas, construindo implementações default;
assim sendo os IEstado representam as evoluções (transições) do nosso modelo.
(3)O EstadoAdapter, serve então para:
é uma classe intermédia/genérica que vai implementar os comportamentos default, sem qualquer actualização de estado, para evitar a programação destas acções em estados onde não fazem sentido (normalmente ficam no mesmo estado).
só depois e em cada um dos estados é que os vamos implementar, pois só se justifica no contexto desse estado haver uma evolução na maquina de estados, e é em cada um desses estados que fica implementado
esta classe implementa o IEstado (implements)
implementa todos os métodos abstractos / default (return this), isto é, não existe evolução de estado/fica no mesmo estado.
surge aqui também os testes da evolução, assim estes estados vão ter que ter acesso aos dados do jogo (JogoDados), construtor e get e set
esta classe deve ser public abstract, para não ser instanciada,
e o JogoDados fica protected, evitando fazer uso do método getJogo, e pelas derivadas acedo a esta instância
(4)E agora construir os estados, que são classes derivadas e:
são extends EstadoAdapter
por ser protected a base, esta derivada tem que ter o construtor correspondente (super)
implementar os métodos correspondentes às acções que fazem sentido, são as setas de saída;
os outros não fazem sentido num estado, ficam com o default do EstadoAdapter
os métodos implementados em cada um dos estados fazem então um return new estado, ou this se voltar ao mesmo, sendo que é implementada também a lógica de cada um dos métodos
Surge uma classe
surge então o inicio da máquina de estados, representando numa classe que inicia o primeiro estado por onde arranca (new..), e que vai permitir aceder (get) ou alterar o estado (set)
esta classe vai ter que satisfazer as ordens do utilizador, define os métodos que permitem alterar/mudança de estado (métodos de transição)
Na iteração com o utilizador (texto ou gráfica):
recebe a maquina de estados;
obtém permanente o estado em que se encontra a máquina de estados;
faz uso de instanceof dos estados para invocar a interface que deve surgir ao utilizador.
neste exemplo:
(1)o JogoMaqEstados, gestão/ver a evolução/mudança entre os estados (no diagrama ir de um estado para outro), conhece o estado atual, é a classe de interface entre o IU do utilizador e todas as classes internas (quer aquelas que representam os estados, quer os dados, que representam o jogo (tem assim informação sobre o jogo (JogoDados), e o estado em que estamos (IEstado));
o JogoMaqEstados tem ainda a interface para todos os outros estados, onde actualiza a situação do estado;
os estados, recebem a entidade jogo, como referência;
já a grelha, onde estão as peças, o jogador, este tipo de dados ficam em JogoDados;
JogoDados, representa assim o tabuleiro do jogo, onde se fazem todas as verificações;
Jogador, representada cada um dos jogos;
Peca, as peças do tabuleiro de jogo;
IEstado;
EstadoAdapter
(10) A interface (IU) também precisa de ir buscar coisas ao jogo, mas quem trata disso é o JogoMaqEstados
package me_jogopecasr; import me_jogopecasr.iu.texto.IUtexto; import me_jogopecasr.logica.JogoMaqEstados; public class Me_jogoPecasR { public static void main(String[] args) { IUtexto iuTexto = new IUtexto(new JogoMaqEstados()); iuTexto.corre(); } }
package me_jogopecasr.iu.texto; import java.util.Scanner; import me_jogopecasr.logica.JogoMaqEstados; import me_jogopecasr.logica.estados.AguardaColocacao; import me_jogopecasr.logica.estados.AguardaDevolucao; import me_jogopecasr.logica.estados.AguardaInicio; import me_jogopecasr.logica.estados.IEstado; public class IUtexto { private JogoMaqEstados jogo; private boolean sair = false; public IUtexto(JogoMaqEstados jogo) { this.jogo = jogo; } void iuAguardaInicio() { if ((jogo.getJogador1() != null && jogo.getJogador1().isGanhou())) { System.out.println("\n" + jogo.getJogador1() + "\n" + jogo.grelhaToString()); } else if (jogo.getJogador2() != null && jogo.getJogador2().isGanhou()) { System.out.println("\n" + jogo.getJogador2() + "\n" + jogo.grelhaToString()); } System.out.println("\n=== AGUARDA INICIO ===\n" + (jogo.getJogador1() != null ? "" + jogo.getJogador1() : "") + (jogo.getJogador2() != null ? "" + jogo.getJogador2() : "")); while (true) { System.out.println("\n0 - Sair\n1 - Define nome de jogador\n2 - Comecar jogo"); char c = ' '; Scanner sc = new Scanner(System.in); c = sc.next().charAt(0); if ((c == '0')) { sair = true; return; } if ((c == '1')) { //1 - Define nome de jogador System.out.println("Numero (1 ou 2) e nome do jogador: "); while (!sc.hasNextInt()); int num = sc.nextInt(); System.out.println(" numero: " + num); String nome = sc.next(); jogo.defineNomeJogador(num, nome); System.out.println(" nome: " + nome); return; } if ((c == '2')) { //2 - Comecar jogo System.out.println("Comecar jogo: "); jogo.comecarJogo(); return; } } } void iuAguardaColocacao() { System.out.println("\n=== AGUARDA COLOCACAO === \n" + jogo.getJogador1() + jogo.getJogador2() + "\nJogador activo: " + jogo.getNomeJogadorActivo() + "\n" + jogo.grelhaToString()); System.out.println("\n1 - Jogar : linha coluna\n2 - Abandonar"); char c = ' '; Scanner sc = new Scanner(System.in); c = sc.next().charAt(0); if ((c == '1')) { System.out.print(jogo.getNomeJogadorActivo() + ">"); while (!sc.hasNextInt()); int linha = sc.nextInt(); while (!sc.hasNextInt()); int coluna = sc.nextInt(); jogo.jogar(jogo.getNumJogadorActivo(), linha, coluna); } if ((c == '2')) { jogo.abandonar(jogo.getNumJogadorActivo()); return; } } void iuAguardaDevolucao() { System.out.println("\n=== AGUARDA DEVOLUCAO === \n" + jogo.getJogador1() + jogo.getJogador2() + "\nJogador activo: " + jogo.getNomeJogadorActivo() + "\n" + jogo.grelhaToString()); // System.out.println("\nDevolver : linha coluna\nAbandonar: -1"); System.out.println("\n1 - Devolver : linha coluna\n2 - Abandonar"); char c = ' '; Scanner sc = new Scanner(System.in); c = sc.next().charAt(0); if ((c == '1')) { System.out.print(jogo.getNomeJogadorActivo() + ">"); while (!sc.hasNextInt()); int linha = sc.nextInt(); while (!sc.hasNextInt()); int coluna = sc.nextInt(); jogo.devolver(jogo.getNumJogadorActivo(), linha, coluna); } if ((c == '2')) { jogo.abandonar(jogo.getNumJogadorActivo()); return; } } public void corre() { while (!sair) { IEstado estado = jogo.getEstado(); if (estado instanceof AguardaInicio) { iuAguardaInicio(); } else if (estado instanceof AguardaColocacao) { iuAguardaColocacao(); } else if (estado instanceof AguardaDevolucao) { iuAguardaDevolucao(); } } } }
package me_jogopecasr.logica; public interface Constantes { int DIM = 3; }
package me_jogopecasr.logica; import java.util.ArrayList; import java.util.List; public class Jogador implements Constantes { private JogoDados jogo; private String nome; private List<Peca> mao = new ArrayList<Peca>(); private boolean ganhou; public Jogador(String nome, JogoDados j) { this.nome = nome; this.jogo = j; ganhou = false; } public void setNome(String nome) { this.nome = nome; } public String getNome() { return nome; } public boolean isGanhou() { return ganhou; } public void setGanhou(boolean ganhou) { this.ganhou = ganhou; } public void recebePecas() { mao.clear(); for (int i = 0; i < DIM; i++) { mao.add(new Peca(this)); } ganhou = false; } public void recebePeca(Peca peca) { mao.add(peca); } public boolean temPecas() { return mao.size() > 0; } public boolean jogar(int linha, int coluna) { if (mao.size() == 0) { return false; } Peca peca = mao.get(0); if (jogo.setPeca(peca, linha, coluna)) { // jogou mao.remove(0); if (jogo.ganhou(this)) { ganhou = true; } return true; } return false; } @Override public String toString() { return "Jogador " + nome + " mao=" + mao + (ganhou ? " == GANHOU ==" : "") + "\n"; } public int getNumPecas() { return mao.size(); } }
package me_jogopecasr.logica; import java.util.ArrayList; import java.util.List; public class JogoDados implements Constantes{ private List<Jogador> jogadores = new ArrayList<Jogador>(); private int numJogActivo; private Peca[][] grelha; public JogoDados() { jogadores.add(new Jogador("A", this)); jogadores.add(new Jogador("B", this)); grelha = new Peca[DIM][DIM]; } public Jogador getJogadorActivo() { return jogadores.get(numJogActivo - 1); } public String getNomeJogadorActivo() { return jogadores.get(numJogActivo - 1).getNome(); } public Jogador getJogadorNaoActivo() { if (numJogActivo == 1) { return jogadores.get(1); } else if (numJogActivo == 2) { return jogadores.get(0); } return null; } public int getNumJogadorActivo() { return numJogActivo; } public int getNumJogadorNaoActivo() { return (numJogActivo == 1? 1: 2); } public Jogador getJogador1() { return jogadores.get(0); } public Jogador getJogador2() { return jogadores.get(1); } public Peca getPeca(int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return null; } if (grelha == null) { return null; } return grelha[linha][coluna]; } public boolean setPeca(Peca peca, int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if (grelha[linha][coluna] != null) { return false; } grelha[linha][coluna] = peca; return true; } public boolean retiraPeca(int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if (grelha[linha][coluna] == null) { return false; } grelha[linha][coluna] = null; return true; } public void jogaOutro() { numJogActivo = numJogActivo == 1 ? 2 : 1; } public boolean inicializa() { grelha = new Peca[DIM][DIM]; jogadores.get(0).recebePecas(); jogadores.get(1).recebePecas(); numJogActivo = 1; return true; } public boolean setNomeJogador(int num, String nome) { try{ jogadores.get(num-1).setNome(nome); return true; }catch(IndexOutOfBoundsException e){ return false; } } private boolean isEmDiagonalPrincipal(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (grelha[i][i] == null || grelha[i][i].getJogador() != jogador) { return false; } } return true; } private boolean isEmDiagonalSecundaria(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (grelha[i][DIM - 1 - i] == null || grelha[i][DIM - 1 - i].getJogador() != jogador) { return false; } } return true; } private boolean isEmDiagonal(Jogador jogador) { return isEmDiagonalPrincipal(jogador) || isEmDiagonalSecundaria(jogador); } private boolean isEmHorizontal(Jogador jogador, int linha) { for (Peca peca : grelha[linha]) { if (peca == null || peca.getJogador() != jogador) { return false; } } return true; } private boolean isEmVertical(Jogador jogador, int coluna) { for (int i = 0; i < grelha[coluna].length; i++) { Peca peca = grelha[i][coluna]; if (peca == null || peca.getJogador() != jogador) { return false; } } return true; } private boolean isEmHorizontal(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (isEmHorizontal(jogador, i)) { return true; } } return false; } private boolean isEmVertical(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (isEmVertical(jogador, i)) { return true; } } return false; } public boolean ganhou(Jogador jogador) { return isEmHorizontal(jogador) || isEmDiagonal(jogador) || isEmVertical(jogador); } public boolean jogar(int numeroJogador, int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if(numJogActivo != numeroJogador){ return false; } Jogador j = getJogadorActivo(); if(!j.jogar(linha, coluna)){ return false; } return true; } public boolean devolver(int numeroJogador, int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if(numJogActivo != numeroJogador){ return false; } Peca peca = getPeca(linha, coluna); Jogador j = getJogadorActivo(); if (peca == null || peca.getJogador() != j) { return false; } if(!retiraPeca(linha, coluna)){ return false; } j.recebePeca(peca); return true; } public String grelhaToString() { String s = ""; for (int i = 0; i < DIM; i++) { for (int j = 0; j < DIM; j++) { String casa; if (grelha[i][j] != null) { casa = "" + grelha[i][j].getJogador().getNome().charAt(0); } else { casa = " "; } s += "|\t" + casa + "\t"; } s += "|\n"; } return s; } public boolean acabou() { return ganhou(jogadores.get(0)) || ganhou(jogadores.get(1)); } }
package me_jogopecasr.logica; import me_jogopecasr.logica.estados.AguardaInicio; import me_jogopecasr.logica.estados.IEstado; public class JogoMaqEstados { JogoDados jogo; IEstado estado; public JogoMaqEstados() { //1) this.jogo = new JogoDados(); //7 this.estado = new AguardaInicio(jogo); } public JogoDados getJogo() { return jogo; } public void setJogo(JogoDados jogo) { this.jogo = jogo; } public IEstado getEstado() { return estado; } public void setEstado(IEstado estado) { this.estado = estado; } //8 redirecionar, agulhar, as acções para os estados, o interface para todas as outras classes public void defineNomeJogador(int numeroJogador, String nome) { //8.1 a evolução do estado, retorna o estado seguinte estado = estado.defineNomeJogador(numeroJogador, nome); //ou usar o setEstado } public void comecarJogo() { //no infitivo estado = estado.comecaJogo(); } public void jogar(int numeroJogador, int linha, int coluna) { estado = estado.joga(numeroJogador, linha, coluna); } public void devolver(int numeroJogador, int linha, int coluna) { estado = estado.devolve(numeroJogador, linha, coluna); } public void abandonar(int numeroJogador) { estado = estado.abandona(numeroJogador); } //9 redirencuionar para o modelo de dados public Jogador getJogador1() { return jogo.getJogador1(); } public Jogador getJogador2() { return jogo.getJogador2(); } //11 redirencuionar para o modelo de dados public String getNomeJogadorActivo() { return jogo.getNomeJogadorActivo(); } public String grelhaToString() { return jogo.grelhaToString(); } public int getNumJogadorActivo() { return jogo.getNumJogadorActivo(); } }
package me_jogopecasr.logica; public class Peca { private Jogador jogador; public Peca(Jogador jogador) { this.jogador = jogador; } public Jogador getJogador() { return jogador; } public String toString(){ return "" + jogador.getNome().charAt(0); } }
package me_jogopecasr.logica.estados; import me_jogopecasr.logica.JogoDados; //5 public class AguardaColocacao extends EstadoAdapter{ //6.2.3 ver se ele não colocou no mesmo local int l, c; //5.1 public AguardaColocacao(JogoDados jogo) { super(jogo); //6.2.5 l = c-1; } //6.2.4 public AguardaColocacao(JogoDados jogo, int l, int c) { super(jogo); this.l = l; this.c = c; } //5.2 @Override public IEstado abandona(int numeroJogador) { //poderia ver qual é o jogador e atribuir a vitória ao outro return new AguardaInicio(jogo); } //5.3 @Override public IEstado joga(int numeroJogador, int linha, int coluna) { //6.2.6, testar se é a mesma jogada if(linha == l && coluna == c){ return this; } //5.3.1, vamos realizar o jogar if(!jogo.jogar(numeroJogador, linha, coluna)){ return this; } //5.3.2, ver se ele ganhou if(jogo.getJogadorActivo().isGanhou()){ return new AguardaInicio(jogo); } //5.3.3, mudar de jogador jogo.jogaOutro(); //5.3.4, ver se tem peças if(jogo.getJogadorActivo().isGanhou()){ return new AguardaColocacao(jogo); //ou return this, fica no mesmo estado, e com l e c a -1 } //5.3.5, return new AguardaDevolucao(jogo); } }
package me_jogopecasr.logica.estados; import me_jogopecasr.logica.JogoDados; //6 public class AguardaDevolucao extends EstadoAdapter { public AguardaDevolucao(JogoDados jogo) { super(jogo); } //6.1 @Override public IEstado abandona(int numeroJogador) { return new AguardaInicio(jogo); } //6.2 @Override public IEstado devolve(int numeroJogador, int linha, int coluna) { //6.2.1 adcionar a devolção if(jogo.devolver(numeroJogador, linha, coluna)){ //o devolver já verifica return new AguardaColocacao(jogo, linha, coluna); //6.2.7 } //6.2.2 fica no mesmo estado return this; } }
package me_jogopecasr.logica.estados; import me_jogopecasr.logica.JogoDados; public class AguardaInicio extends EstadoAdapter{ //4) por ser protected a base, esta derivada tem que ter um construtor correspondente public AguardaInicio(JogoDados jogo) { super(jogo); } //4.2) implementar os métodos correspondentes às acções que fazem sentido, são as setas de saída @Override public IEstado defineNomeJogador(int numeroJogador, String nome) { //4.4 jogo.setNomeJogador(numeroJogador, nome); //4.3 return this; } @Override public IEstado comecaJogo() { //4.4 testes para começar um jogo if(jogo.getJogador1()== null || jogo.getJogador2() ==null){ //fico no mesmo estado return this; } //12 jogo.inicializa(); return new AguardaColocacao(jogo); } }
package me_jogopecasr.logica.estados; //3) existem acções que não fazem sent import me_jogopecasr.logica.JogoDados; public abstract class EstadoAdapter implements IEstado{ //3.2) acesso aos jogos de dados //3.3) protected, evitando fazer uso do método getJogo, e pelas derivadas acedo a esta instancia protected JogoDados jogo; public EstadoAdapter(JogoDados jogo) { this.jogo = jogo; } public JogoDados getJogo() { return jogo; } public void setJogo(JogoDados jogo) { this.jogo = jogo; } //3.1) @Override public IEstado defineNomeJogador(int numeroJogador, String nome) { return this; } @Override public IEstado comecaJogo() { return this; } @Override public IEstado joga(int numeroJogador, int linha, int coluna) { return this; } @Override public IEstado devolve(int numeroJogador, int linha, int coluna) { return this; } @Override public IEstado abandona(int numeroJogador) { return this; } }
package me_jogopecasr.logica.estados; //2) definir todos os estados public interface IEstado { IEstado defineNomeJogador(int numeroJogador, String nome); IEstado comecaJogo(); IEstado joga(int numeroJogador, int linha, int coluna); IEstado devolve(int numeroJogador, int linha, int coluna); IEstado abandona(int numeroJogador); }
Programação avançada – capitulo 8 – maquina de estados, exemplo3
Exemplo de uma maquina de estado, usando como referência a implementação de um jogo de 3×3, onde existem dois jogadores.
O diagrama da maquina de estados dado:
Outra versão do diagrama da maquina de estados:
package me_jogopecas; import me_jogopecas.iu.texto.IUTexto; import me_jogopecas.logica.estados.JogoMaquinaEstados; public class Me_jogoPecas { public static void main(String[] args) { IUTexto iuTexto = new IUTexto(new JogoMaquinaEstados()); iuTexto.corre(); } }
package me_jogopecas.iu.texto; import java.util.Scanner; import me_jogopecas.logica.estados.*; public class IUTexto { private JogoMaquinaEstados jogo; private boolean sair = false; public IUTexto(JogoMaquinaEstados jogo) { this.jogo = jogo; } void iuAguardaInicio() { if ((jogo.getJogador1() != null && jogo.getJogador1().isGanhou())) { System.out.println("\n" + jogo.getJogador1() + "\n" + jogo.grelhaToString()); } else if (jogo.getJogador2() != null && jogo.getJogador2().isGanhou()) { System.out.println("\n" + jogo.getJogador2() + "\n" + jogo.grelhaToString()); } System.out.println("\n=== AGUARDA INICIO ===\n" + (jogo.getJogador1() != null ? "" + jogo.getJogador1() : "") + (jogo.getJogador2() != null ? "" + jogo.getJogador2() : "")); while (true) { System.out.println("\n0 - Sair\n1 - Define nome de jogador\n2 - Comecar jogo\n3 - Ler jogo"); char c = ' '; Scanner sc = new Scanner(System.in); c = sc.next().charAt(0); if ((c == '0')) { sair = true; return; } if ((c == '1')) { //1 - Define nome de jogador System.out.println("Numero (1 ou 2) e nome do jogador: "); while (!sc.hasNextInt()); int num = sc.nextInt(); System.out.println(" numero: " + num); String nome = sc.next(); jogo.defineNomeJogador(num, nome); System.out.println(" nome: " + nome); return; } if ((c == '2')) { //2 - Comecar jogo System.out.println("Comecar jogo: "); jogo.comecarJogo(); return; } if ((c == '3')) { //3 - Ler o jogo System.out.println("Ler o jogo: "); String ficheiroLer = sc.next(); // jogo.ler(ficheiroLer); return; } } } void iuAguardaColocacao() { System.out.println("\n=== AGUARDA COLOCACAO === \n" + jogo.getJogador1() + jogo.getJogador2() + "\nJogador activo: " + jogo.getNomeJogadorActivo() + "\n" + jogo.grelhaToString()); System.out.println("\n1 - Jogar : linha coluna\n2 - Abandonar"); char c = ' '; Scanner sc = new Scanner(System.in); c = sc.next().charAt(0); if ((c == '1')) { System.out.print(jogo.getNomeJogadorActivo() + ">"); while (!sc.hasNextInt()); int linha = sc.nextInt(); while (!sc.hasNextInt()); int coluna = sc.nextInt(); jogo.jogar(jogo.getNumJogadorActivo(), linha, coluna); } if ((c == '2')) { System.out.println("Guardar o jogo: "); String ficheiroGuardar = sc.next(); // jogo.grava(ficheiroGuardar); jogo.abandonar(jogo.getNumJogadorActivo()); return; } } void iuAguardaDevolucao() { System.out.println("\n=== AGUARDA DEVOLUCAO === \n" + jogo.getJogador1() + jogo.getJogador2() + "\nJogador activo: " + jogo.getNomeJogadorActivo() + "\n" + jogo.grelhaToString()); // System.out.println("\nDevolver : linha coluna\nAbandonar: -1"); System.out.println("\n1 - Devolver : linha coluna\n2 - Abandonar"); char c = ' '; Scanner sc = new Scanner(System.in); c = sc.next().charAt(0); if ((c == '1')) { System.out.print(jogo.getNomeJogadorActivo() + ">"); while (!sc.hasNextInt()); int linha = sc.nextInt(); while (!sc.hasNextInt()); int coluna = sc.nextInt(); jogo.devolver(jogo.getNumJogadorActivo(), linha, coluna); } if ((c == '2')) { jogo.abandonar(jogo.getNumJogadorActivo()); return; } } public void corre() { while (!sair) { IEstado estado = jogo.getEstado(); if (estado instanceof AguardaInicio) { iuAguardaInicio(); } else if (estado instanceof AguardaColocacao) { iuAguardaColocacao(); } else if (estado instanceof AguardaDevolucao) { iuAguardaDevolucao(); } } } }
package me_jogopecas.logica; public interface Constantes { int DIM =3; }
package me_jogopecas.logica; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import static me_jogopecas.logica.Constantes.*; public class Jogador implements Constantes, Serializable{ private String nome; private JogoDados jogo; //lista de peças no inicio private List<Peca> mao = new ArrayList<Peca>(); private boolean ganhou; public Jogador(String nome, JogoDados j) { this.nome = nome; this.jogo = j; ganhou = false; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public JogoDados getJogo() { return jogo; } public void setJogo(JogoDados jogo) { this.jogo = jogo; } public List<Peca> getMao() { return mao; } public void setMao(List<Peca> mao) { this.mao = mao; } public boolean isGanhou() { return ganhou; } public void setGanhou(boolean ganhou) { this.ganhou = ganhou; } //quando começar o jogo! public void recebePecas() { mao.clear(); for (int i = 0; i < DIM; i++) { mao.add(new Peca(this)); } ganhou = false; } public int getNumPecas(){ return mao.size(); } //quando a peça é devolvida public void recebePeca(Peca peca) { mao.add(new Peca(this)); } public boolean temPecas() { return mao.size() > 0; } public boolean jogar(int linha, int coluna){ if(mao.size()==0){ return false; } //as peças são todas iguais Peca peca = mao.get(0); if(jogo.setPeca(peca, linha, coluna)){ //jogou mao.remove(0); if(jogo.ganhou(this)){ ganhou = true; } return true; } return false; } @Override public String toString() { return "Jogador " + nome + " mao=" + mao + (ganhou ? " == GANHOU ==" : "") + "\n" ; } }
package me_jogopecas.logica; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class JogoDados implements Constantes, Serializable { private List<Jogador> jogadores = new ArrayList<Jogador>(); private int numJoActivo; private Peca[][] grelha; public JogoDados() { jogadores.add(new Jogador("A", this)); jogadores.add(new Jogador("B", this)); grelha = new Peca[DIM][DIM]; } public Jogador getJogadorActivo() { //considera-se que o jogador activo é 1 ou 2, daí o -1 return jogadores.get(numJoActivo - 1); } public String getNomeJogadorActivo() { return jogadores.get(numJoActivo - 1).getNome(); } public Jogador getJogadorNaoActivo() { if (numJoActivo == 1) { return jogadores.get(1); } else if (numJoActivo == 2) { return jogadores.get(0); } return null; } public int getNumJogadorActivo() { return numJoActivo; } public int getNumJogadorNaoActivo() { return (numJoActivo == 1 ? 1 : 2); } public Jogador getJogador1() { return jogadores.get(0); } public Jogador getJogador2() { return jogadores.get(1); } public Peca getPeca(int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return null; } if (grelha == null) { return null; } return grelha[linha][coluna]; } public boolean setPeca(Peca peca, int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if (grelha[linha][coluna] != null) { return false; } grelha[linha][coluna] = peca; return true; } public boolean retiraPeca(int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if (grelha[linha][coluna] == null) { return false; } grelha[linha][coluna] = null; return true; } public void jogaOutro() { numJoActivo = numJoActivo == 1 ? 2 : 1; } public boolean inicializa() { grelha = new Peca[DIM][DIM]; jogadores.get(0).recebePecas(); jogadores.get(1).recebePecas(); numJoActivo = 1; return true; } public boolean setNomeJogador(int num, String nome) { //falta validar se o num é válido try { //tento aceder a um elemento do array que nao existe jogadores.get(num - 1).setNome(nome); return true; } catch (IndexOutOfBoundsException e) { return false; } } private boolean isEmDiagonalPrincipal(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (grelha[i][i] == null || grelha[i][i].getJogador() != jogador) { return false; } } return true; } private boolean isEmDiagonalSecundaria(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (grelha[i][DIM - 1 - i] == null || grelha[i][DIM - 1 - i].getJogador() != jogador) { return false; } } return true; } private boolean isEmDiagonal(Jogador jogador) { return isEmDiagonalPrincipal(jogador) || isEmDiagonalSecundaria(jogador); } private boolean isEmHorizontal(Jogador jogador, int linha) { for (Peca peca : grelha[linha]) { if (peca == null || peca.getJogador() != jogador) { return false; } } return true; } private boolean isEmVertical(Jogador jogador, int coluna) { for (int i = 0; i < grelha[coluna].length; i++) { Peca peca = grelha[i][coluna]; if (peca == null || peca.getJogador() != jogador) { return false; } } return true; } private boolean isEmHorizontal(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (isEmHorizontal(jogador, i)) { return true; } } return false; } private boolean isEmVertical(Jogador jogador) { for (int i = 0; i < DIM; i++) { if (isEmVertical(jogador, i)) { return true; } } return false; } public boolean ganhou(Jogador jogador) { return isEmHorizontal(jogador) || isEmDiagonal(jogador) || isEmVertical(jogador); } public boolean jogar(int numeroJogador, int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if (numJoActivo != numeroJogador) { return false; } Jogador j = getJogadorActivo(); if (!j.jogar(linha, coluna)) { return false; } return true; } public boolean devolver(int numeroJogador, int linha, int coluna) { if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) { return false; } if (numJoActivo != numeroJogador) { return false; } Peca peca = getPeca(linha, coluna); Jogador j = getJogadorActivo(); if (peca == null || peca.getJogador() != j) { return false; } if (!retiraPeca(linha, coluna)) { return false; } j.recebePeca(peca); return true; } public String grelhaToString() { String s = ""; for (int i = 0; i < DIM; i++) { for (int j = 0; j < DIM; j++) { String casa; if (grelha[i][j] != null) { casa = "" + grelha[i][j].getJogador().getNome().charAt(0); } else { casa = " "; } s += "|\t" + casa + "\t"; } s += "|\n"; } return s; } public boolean acabou() { return ganhou(jogadores.get(0)) || ganhou(jogadores.get(1)); } }
package me_jogopecas.logica; import java.io.Serializable; public class Peca implements Serializable { private Jogador jogador; public Peca(Jogador jgd) { this.jogador = jgd; } public Jogador getJogador() { return jogador; } public void setJogador(Jogador jogador) { this.jogador = jogador; } @Override public String toString() { return "" + jogador.getNome().charAt(0); } }
package me_jogopecas.logica.estados; import me_jogopecas.logica.JogoDados; public class AguardaColocacao extends EstadoAdapter { //para informação adicional int l, c; public AguardaColocacao(JogoDados jogodados) { super(jogodados); //para informação adicional l = c = -1; //se vier return this } //para informação adicional public AguardaColocacao(JogoDados jogo, int l, int c) { super(jogo); this.l = l; this.c = c; } //setas de saida @Override public IEstado abandona(int numeroJogador) { //podiamos atribuir a vitoria a um jogador, olhando para numeroJogador //voltamos ao inicio return new AguardaInicio(jogodados); } @Override public IEstado joga(int numeroJogador, int linha, int coluna) { //informação adicional if (linha == l && coluna == c) { return this; } //jogar normal, o jogar faz o teste se pode ou nao jogar jogodados.jogar(numeroJogador, linha, coluna); //verificar se o jogador activo if (jogodados.getJogadorActivo().isGanhou()) { return new AguardaInicio(jogodados); } jogodados.jogaOutro(); //jogar a um jogador que não tem peças //jogar que nao tem pecas if (jogodados.getJogadorActivo().temPecas()) { //return this; //tb funciona //para informação adicional return new AguardaColocacao(jogodados); } return new AguardaDevolucao(jogodados); } }
package me_jogopecas.logica.estados; import me_jogopecas.logica.JogoDados; public class AguardaDevolucao extends EstadoAdapter { public AguardaDevolucao(JogoDados jogodados) { super(jogodados); } @Override public IEstado abandona(int numeroJogador) { return new AguardaInicio(jogodados); } @Override public IEstado devolve(int numeroJogador, int linha, int coluna) { if (jogodados.devolver(numeroJogador, linha, coluna)) { //return new AguardaColocacao(jogodados); //informação adicional return new AguardaColocacao(jogodados, linha, coluna); } return this; } }
package me_jogopecas.logica.estados; import me_jogopecas.logica.JogoDados; public class AguardaInicio extends EstadoAdapter { //construtor que redireciona para a classe base public AguardaInicio(JogoDados jogodados) { super(jogodados); } //setas de saida, os outros não fazem sentido @Override public IEstado defineNomeJogador(int numeroJogador, String nome) { //qual o novo estado depois de defineNomeJogador //agora vou atuar sobre o jogo jogodados.setNomeJogador(numeroJogador, nome); return this; //fico no mesmo estado } @Override public IEstado comecaJogo() { //testes para verificar se já tinhamos os dois jogadores if(jogodados.getJogador1()==null || jogodados.getJogador2()==null){ return this; } jogodados.inicializa(); return new AguardaColocacao(jogodados); } }
package me_jogopecas.logica.estados; import java.io.Serializable; import me_jogopecas.logica.JogoDados; //abstract para nao ser instanciada public abstract class EstadoAdapter implements IEstado, Serializable{ JogoDados jogodados;//tem que ter acesso aos dados de jogo //podemos colocar como protected, para ser usado apenas nas classes derivadas public EstadoAdapter(JogoDados jogod) { this.jogodados = jogod; } public JogoDados getJogodados() { return jogodados; } public void setJogodados(JogoDados jogodados) { this.jogodados = jogodados; } @Override public IEstado defineNomeJogador(int numeroJogador, String nome) { return this; } @Override public IEstado comecaJogo() { return this; } @Override public IEstado joga(int numeroJogador, int linha, int coluna) { return this; } @Override public IEstado devolve(int numeroJogador, int linha, int coluna) { return this; } @Override public IEstado abandona(int numeroJogador) { return this; } //o interface precisa de ir buscar coisas para as mostrar }
package me_jogopecas.logica.estados; public interface IEstado { //definir todas as acções o jogo IEstado defineNomeJogador(int numeroJogador, String nome); IEstado comecaJogo(); IEstado joga(int numeroJogador, int linha, int coluna); IEstado devolve(int numeroJogador, int linha, int coluna); IEstado abandona(int numeroJogador); }
package me_jogopecas.logica.estados; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import me_jogopecas.logica.Jogador; import me_jogopecas.logica.JogoDados; public class JogoMaquinaEstados implements Serializable { //se JogoMaquinaEstados é Serializable as classes de base tb têm que ser JogoDados jogodados; IEstado estado; public JogoMaquinaEstados() { //criar o meu jogo this.jogodados = new JogoDados(); //desenvolver, evoluir, programação dos estados (do diagrama) this.estado = new AguardaInicio(jogodados); } public IEstado getEstado() { return estado; } public void setEstado(IEstado estado) { this.estado = estado; } //implementar todos os estados, através desta maquina de estados //interface de todas as classes public void defineNomeJogador(int numeroJogador, String nome) { //estado seguinte vai ser do retornar.. estado = estado.defineNomeJogador(numeroJogador, nome); } public void comecarJogo() { estado = estado.comecaJogo(); } public void jogar(int numeroJogador, int linha, int coluna) { estado = estado.joga(numeroJogador, linha, coluna); } public void devolver(int numeroJogador, int linha, int coluna) { estado = estado.devolve(numeroJogador, linha, coluna); } public void abandonar(int numeroJogador) { estado = estado.abandona(numeroJogador); } //os métodos que surgem no IUTexto public Jogador getJogador1() { return jogodados.getJogador1(); } public Jogador getJogador2() { return jogodados.getJogador2(); } public String getNomeJogadorActivo() { return jogodados.getNomeJogadorActivo(); } public int getNumJogadorActivo() { return jogodados.getNumJogadorActivo(); } public String grelhaToString() { return jogodados.grelhaToString(); } //ou em alternativa dava acesso ao jogodados public JogoDados getJogo() { return jogodados; } //gravação dos ficheiros public void grava(String filename) throws IOException { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream(filename)); oos.writeObject(this); } finally { if(oos != null) { oos.close(); } } } public static JogoMaquinaEstados ler(String filename) throws IOException, ClassNotFoundException { ObjectInputStream oos = null; try { oos = new ObjectInputStream(new FileInputStream(filename)); return (JogoMaquinaEstados) oos.readObject(); } finally { if(oos != null) { oos.close(); } } } }
Programação avançada – capitulo 8 – maquina de estados, exemplo2
Exemplo de uma maquina de estado, simples, usando as referências a um mecanismo de um elevador com dois estados extra: erro e chaveManutencao
Em que ficou:
Com:
package me_me_elevador; import me_me_elevador.IU.Texto.UsaElevador; import me_me_elevador.logica.Elevador; public class Me_ME_elevador { public static void main(String[] args) { Elevador e = new Elevador(); UsaElevador usaE = new UsaElevador(e); //interface com o utilizador usaE.corre(); } }
package me_me_elevador.IU.Texto; import me_me_elevador.logica.*; import me_me_elevador.util.IUUtil; public class UsaElevador { //o interface com o utilizador, com o nosso elevador. private Elevador elevador; private boolean sair = false; public UsaElevador(Elevador elevador) { this.elevador = elevador; } public void corre() { while (!sair) { //saber em que estado estou IEstado estado = elevador.getEstado(); if (estado instanceof RC) { iuRC(); } else if (estado instanceof Andar1) { iuAndar1(); } else if (estado instanceof Andar2) { iuAndar2(); } else if (estado instanceof Andar3) { iuAndar3(); }else if (estado instanceof Manutencao) { iuManutencao(); } } } private void iuManutencao() { int opcao = IUUtil.getOpcao("Em modo de manutenção", "Usar a chave", "Sair do elevador"); switch (opcao) { case 0: elevador.chaveManutencao(); case 1: sair = true; break; } } private void iuRC() { //opcoa 1 //pedir ao utilizador: sair ou subir int opcao = IUUtil.getOpcao("Piso R/C", "Sair do prédio", "Subir", "Erro"); switch (opcao) { case 0: sair = true; break; case 1: //fazer evoluir a maquina de estados elevador.sobe(); break; case 2: elevador.erro(); break; } } private void iuAndar1() { int opcao = IUUtil.getOpcao("Piso 1 andar", "Descer", "Subir", "Erro"); switch (opcao) { case 0: //fazer evoluir a maquina de estados elevador.desce(); break; case 1: //fazer evoluir a maquina de estados elevador.sobe(); break; case 2: elevador.erro(); break; } } private void iuAndar2() { int opcao = IUUtil.getOpcao("Piso 2 andar", "Descer", "Subir", "Erro"); switch (opcao) { case 0: //fazer evoluir a maquina de estados elevador.desce(); break; case 1: //fazer evoluir a maquina de estados elevador.sobe(); break; case 2: elevador.erro(); break; } } private void iuAndar3() { int opcao = IUUtil.getOpcao("Piso 3 andar", "Descer", "Erro"); switch (opcao) { case 0: //fazer evoluir a maquina de estados elevador.desce(); break; case 1: elevador.erro(); break; } } }
package me_me_elevador.logica; public class Andar1 extends Piso { @Override public IEstado sobe() { //altero de estado return new Andar2(); } @Override public IEstado desce() { //altero de estado return new RC(); } //se fossem todos iguais podia ser implementado no Piso // com return this; @Override public IEstado erro() { return new Manutencao(this); } }
package me_me_elevador.logica; public class Andar2 extends Piso{ @Override public IEstado sobe() { return new Andar3(); } @Override public IEstado desce() { return new Andar1(); } @Override public IEstado erro() { return new Manutencao(this); } }
package me_me_elevador.logica; public class Andar3 extends Piso{ @Override public IEstado sobe() { //fico no mesmo estado return this; } @Override public IEstado desce() { //altero de estado return new Andar2(); } @Override public IEstado erro() { return new Manutencao(this); } }
package me_me_elevador.logica; public class Elevador { IEstado estado; public Elevador() { estado = new RC(); } public IEstado getEstado() { return estado; } public void setEstado(IEstado estado) { this.estado = estado; } public void sobe() { setEstado(estado.sobe()); } public void desce() { setEstado(estado.desce()); } public void erro(){ setEstado(estado.erro()); } public void chaveManutencao(){ setEstado(estado.chaveManutencao()); } }
package me_me_elevador.logica; public interface IEstado { IEstado sobe(); IEstado desce(); IEstado erro(); IEstado chaveManutencao(); }
package me_me_elevador.logica; public class Manutencao extends Piso{ //vai recordar qual era o estado antes private IEstado anterior; public Manutencao(IEstado ant) { this.anterior = ant; } @Override public IEstado chaveManutencao() { return anterior; } }
package me_me_elevador.logica; public abstract class Piso implements IEstado { @Override public IEstado sobe() { return this; } @Override public IEstado desce() { return this; } @Override public IEstado chaveManutencao() { return this; } @Override public IEstado erro() { return this; } }
package me_me_elevador.logica; public class RC extends Piso { @Override public IEstado sobe() { //altero de estado return new Andar1(); } @Override public IEstado desce() { //fico no mesmo estado return this; } @Override public IEstado erro() { return new Manutencao(this); } }
package me_me_elevador.util; import java.util.Scanner; public class IUUtil { public static int getOpcao(String question, String... options) { System.out.println(question); char[] ops = new char[options.length]; for (int i = 0; i < options.length; i++) { char o = '\0'; int c = 0; while (c < options[i].length()) { o = Character.toUpperCase(options[i].charAt(c)); for (int j = 0; j < i; j++) { if (ops[j] == o) { o = '\0'; break; } } if (o != '\0') { break; } c++; } if (c >= options[i].length()) { return -1; } ops[i] = o; if (i > 0) { System.out.print(", "); } if (c > 0) { System.out.print(options[i].substring(0, c)); } System.out.print("(" + ops[i] + ")" + options[i].substring(c + 1)); } System.out.println(); Scanner sc = new Scanner(System.in); while (true) { System.out.print("Option: "); char o = Character.toUpperCase(sc.nextLine().charAt(0)); for (int i = 0; i < ops.length; i++) { if (o == ops[i]) { return i; } } } } }
O erro e a chaveEmergencia são acções, sendo que o evento que surge é a Manutenção.
Programação avançada – capitulo 8 – maquina de estados, exemplo1
Exemplo de uma maquina de estado, simples, usando as referências a um mecanismo de um elevador
Em que ficou:
Com:
package me_me_elevador; import me_me_elevador.IU.Texto.UsaElevador; import me_me_elevador.logica.Elevador; public class Me_ME_elevador { public static void main(String[] args) { Elevador e = new Elevador(); UsaElevador usaE = new UsaElevador(e); //interface com o utilizador usaE.corre(); } }
package me_me_elevador.IU.Texto; import me_me_elevador.logica.*; import me_me_elevador.util.IUUtil; public class UsaElevador { //o interface com o utilizador, com o nosso elevador. private Elevador elevador; private boolean sair = false; public UsaElevador(Elevador elevador) { this.elevador = elevador; } public void corre() { while (!sair) { //saber em que estado estou IEstado estado = elevador.getEstado(); if (estado instanceof RC) { iuRC(); } else if (estado instanceof Andar1) { iuAndar1(); } else if (estado instanceof Andar2) { iuAndar2(); } else if (estado instanceof Andar3) { iuAndar3(); } } } private void iuRC() { //opcoa 1 //pedir ao utilizador: sair ou subir int opcao = IUUtil.getOpcao("Piso R/C", "Ir embora", "Subir"); switch (opcao) { case 0: sair = true; break; case 1: //fazer evoluir a maquina de estados elevador.sobe(); break; } } private void iuAndar1() { int opcao = IUUtil.getOpcao("Piso 1 andar","Descer", "Subir"); switch (opcao) { case 0: //fazer evoluir a maquina de estados elevador.desce(); break; case 1: //fazer evoluir a maquina de estados elevador.sobe(); break; } } private void iuAndar2() { int opcao = IUUtil.getOpcao("Piso 2 andar","Descer", "Subir"); switch (opcao) { case 0: //fazer evoluir a maquina de estados elevador.desce(); break; case 1: //fazer evoluir a maquina de estados elevador.sobe(); break; } } private void iuAndar3() { int opcao = IUUtil.getOpcao("Piso 3 andar","Descer"); switch (opcao) { case 0: //fazer evoluir a maquina de estados elevador.desce(); break; } } }
package me_me_elevador.logica; public class Andar1 extends Piso{ @Override public IEstado sobe() { //altero de estado return new Andar2(); } @Override public IEstado desce() { //altero de estado return new RC(); } }
package me_me_elevador.logica; public class Andar2 extends Piso{ @Override public IEstado sobe() { return new Andar3(); } @Override public IEstado desce() { return new Andar1(); } }
package me_me_elevador.logica; public class Andar3 extends Piso{ @Override public IEstado sobe() { //fico no mesmo estado return this; } @Override public IEstado desce() { //altero de estado return new Andar2(); } }
package me_me_elevador.logica; public class Elevador { //é a maquina de estados, que faz alterar os estados IEstado estado; public Elevador() { //o estado incial//arranque é então: estado = new RC(); } public IEstado getEstado() { return estado; } public void setEstado(IEstado estado) { this.estado = estado; } //podia ter outras informações, como o numero de pessoas //as luzes que estão ou não ligadas.. //métodos que dão origem à mudança de estado public void sobe() { setEstado(estado.sobe()); //versão1 //this.estado = estado.sobe(); //versão2 } public void desce() { setEstado(estado.desce()); //versão1 //this.estado = estado.desce(); //versão2 } }
package me_me_elevador.logica; public interface IEstado { //acções comuns a cada uma dos estados:RC,andar1,andar2... //interface geral IEstado sobe(); IEstado desce(); }
package me_me_elevador.logica; public abstract class Piso implements IEstado { //classe para implementação default, sem alterar o estado //serve para simplifcar todas as outras @Override public IEstado sobe() { return this; } @Override public IEstado desce() { return this; } }
package me_me_elevador.logica; public class RC extends Piso{ @Override public IEstado sobe() { //altero de estado return new Andar1(); } @Override public IEstado desce() { //fico no mesmo estado return this; } }
package me_me_elevador.util; import java.util.Scanner; public class IUUtil { // v1 public static int getOpcao(String question, String... options) { System.out.println(question); char[] ops = new char[options.length]; for (int i = 0; i < options.length; i++) { char o = '\0'; int c = 0; while (c < options[i].length()) { o = Character.toUpperCase(options[i].charAt(c)); for (int j = 0; j < i; j++) { if (ops[j] == o) { o = '\0'; break; } } if (o != '\0') { break; } c++; } if (c >= options[i].length()) { return -1; } ops[i] = o; if (i > 0) { System.out.print(", "); } if (c > 0) { System.out.print(options[i].substring(0, c)); } System.out.print("(" + ops[i] + ")" + options[i].substring(c + 1)); } System.out.println(); Scanner sc = new Scanner(System.in); while (true) { System.out.print("Option: "); char o = Character.toUpperCase(sc.nextLine().charAt(0)); for (int i = 0; i < ops.length; i++) { if (o == ops[i]) { return i; } } } } }
No interface IEstado, são colocados todas as ações dos estados, mesmo que não façam sentido em alguns dos estados.
A class Piso, faz a implementação default de alguns dos estados para que não tenhamos que estar a repetir código nos diferentes estados.
Programação avançada – capitulo 8
Num máquina de estados existem os seguintes conceitos:
estados (por exemplo: “sem moedas”, “com moedas”, “em manutenção”, ..),
acções (por exemplo: “insere moeda”, “retira moeda”, “roda manípulo”, “roda manipulo”,…),
tem reacções (de ignorar, ou de alterar o seu estado e executar acções (que por sua vez podem ou não gerar outras acções)).
Assim uma aplicação tem:
contexto ou entidade (entidade cujo contexto depende do seu estado),
eventos (podem acontecer acções que fazem desencadear eventos),
e estados (é a situação que é assumida aquando de eventos e traduz o resultado do comportamento da entidade).
A máquina de estados orientada a objectos
A entidade maquina de estados, tem funções que correspondem ao processamento dos eventos a que reage. O processamento dos eventos depende do estado em que a entidade se encontra. Quando se pretende que o código das funções da entidade que correspondem ao processamento de eventos não envolva instruções if ou switch dependentes do estado:
- define-se uma hierarquia de classes que representa os estados possíveis e contém o conhecimento acerca do processamento de eventos em cada estado (incluindo mudanças de estado)
- a entidade, em cada momento, tem uma referência para um objecto que representa o estado concreto em que se encontra
- as funções da entidade que correspondem a processamento de eventos que dependem do estado, delegam para o objecto que representa o estado corrente, o processamento dos eventos.
O contexto tem uma referência para um objecto que representa o estado (concreto) em que se encontra. Os possíveis estados concretos fazem parte de uma hierarquia que tem por base um tipo abstracto, IEstado.
O comportamento da entidade (o seu contexto) é delegado nesse objecto (estado concreto):
reacção aos eventos
acções a executar
mudanças de estado
Assim surge a entidade ou contexto, o estado e os estados concretos, e que se podem resumir da seguinte forma:
A entidade ou contexto:
define o interface necessário de acordo com as suas responsabilidades
mantém um membro que representa o estado concreto em que se encontra
tem uma referencia para o IEstado (tipo da base da hierarquia que representa os estados possíveis) que refere o objecto que representa o estado concreto em que se encontra
tem métodos que representam o processamento de eventos. A entidade delega o processamento de eventos (que pode variar conforme o seu estado) no objecto que representa o estado corrente
O estado (IEstado):
define o interface para encapsular o comportamento da entidade que pode variar conforme o seu estado concreto
Os Estados concretos, que são classes derivadas da classe EstadoAdapter (que implementa o interface IEstado):
cada classe derivada, representando um estado concreto, implementa o comportamento que lhe e associado.
1 |