MVC e projeto integrador
Model-View-Controller em aplicações event driven e a organização do projeto em equipa.
Nesta página
MVC separa a aplicação em três papéis. O Model guarda o estado e as regras: posições, vidas, pontuação. A View desenha o estado no ecrã e não decide nada. O Controller recebe os eventos (teclas, cliques), atualiza o Model e pede à View para redesenhar. Em aplicações event driven como o jogo, nada corre em sequência fixa: o programa espera eventos e reage.
Porque separar
Sem MVC, a classe do jogo lê teclas, mexe em posições e desenha tudo misturado, e cada mudança toca em tudo. Com MVC, cada pergunta tem um dono: “onde está o herói” é o Model, “como se desenha” é a View, “o que acontece ao premir cima” é o Controller. Os testes agradecem: o Model testa-se sem ecrã nem teclado, como fizeste nos testes unitários.
Mapear o Hero
No jogo do projeto, o mapeamento típico é este:
- Model:
Arenacom o herói, os monstros, as paredes e a pontuação, mais as regras de movimento e colisão. Não importa nenhum pacote de desenho. - View: a classe que pega na
Arenae desenha cada elemento no terminal com a biblioteca do projeto (Lanterna, no caso do jogo Hero). Só lê o Model, nunca o altera. - Controller: o
ControladorArenacom o mapa deComandopor tecla. Recebe o evento, atualiza o Model e chama a View.
O ciclo de um evento fica assim: tecla premida, Controller atualiza a posição no Model, View redesenha a arena. Se um dia trocares Lanterna por outra biblioteca, só a View muda. O repositório oficial da Lanterna documenta a biblioteca de terminal usada no jogo.
Na Lanterna, o esqueleto do ciclo é ler a tecla, atualizar e redesenhar:
Screen screen = new DefaultTerminalFactory().createScreen();
screen.startScreen();
KeyStroke tecla = screen.readInput(); // bloqueia até haver tecla
arena.atualizar(tecla);
desenharArena(screen, arena);
screen.refresh();
screen.stopScreen();
O readInput bloqueia à espera do utilizador: é o “event driven” na prática. O resto do programa é o Model puro que já testaste. Como a Lanterna precisa de um terminal a sério, o bloco executável abaixo usa só Java puro com teclas programadas: a entrada já contém a sequência e o programa imprime o estado a cada passo.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int x = 5;
int pontos = 0;
Scanner teclado = new Scanner(System.in);
while (teclado.hasNextLine()) {
String tecla = teclado.nextLine().trim();
if (tecla.equals("sair")) {
break;
}
if (tecla.equals("direita")) {
x = x + 1;
}
if (tecla.equals("item")) {
pontos = pontos + 50;
}
System.out.println("heroi em " + x + ", pontos " + pontos);
}
teclado.close();
}
}Dados de entrada
A saída mostra o herói a andar para a direita e os pontos a saltar para 50 quando apanha o item. Cada linha impressa é um “redesenho”: o estado muda, o observador vê.
Organizar o projeto em equipa
Três pessoas, seis semanas, um repositório. O que funciona:
- Divide por camadas MVC, uma pessoa por camada, com as interfaces (
Ecra,Comportamento,Comando) acordadas na primeira semana. - Cada funcionalidade num branch com revisão antes do merge, como no controlo de versões.
- Testes escritos junto com o código: o Model com testes unitários puros, o Controller com mocks da View, e a cobertura e o Pitest no build para apanhar testes fracos.
- Refactoring contínuo: quando um smell aparece, trata-o nessa semana. Na última semana já não há tempo para reestruturar.
Exemplo: planear os testes do projeto
Antes de escrever código novo, lista o que vais testar. Para a funcionalidade “herói apanha item e ganha pontos”:
Testa a regra pura, sem ecrã nem teclado. Cria a arena, apanha o item, verifica os 50 pontos e que o item desapareceu. É o teste mais barato e o mais importante: se a regra está errada aqui, está errada em todo o lado.
Testa a reação à tecla com a View em mock. Prende a entrada na tecla certa, corre o passo e verifica com verify que a View recebeu o redesenho esperado. Não testa a regra (isso é do Model), testa a ligação tecla, ação, desenho.
Testa a notificação com um observador espião. Apanha o item e confirma que a pontuação foi notificada com o valor certo. Se um segundo observador for registado a meio, também ele recebe: é o desacoplamento do Observer à prova.
- Model: apanhar o item soma 50 à pontuação e remove o item da arena (teste puro, sem ecrã).
- Controller: a tecla move o herói para a casa do item (mock da View verifica o redesenho).
- Observer: a pontuação é notificada quando o item desaparece (verifica com mock de observador).
Três testes, três camadas, cada um a proteger a sua responsabilidade. Quando o projeto crescer, esta lista vira a bateria que te deixa refatorar sem medo.