Conteúdos da cadeira

Padrões de desenho

Os dez padrões do programa aplicados a problemas concretos do jogo.

Markdown

Perguntar sobre esta página

ChatGPTClaudePerplexityGeminiCopiar e abrir

Envia o link e pede à IA para ler a página. No Gemini, cola a pergunta copiada.

Ver pergunta para copiar
Nesta página

Um padrão de desenho é uma solução reutilizável para um problema recorrente, com nome, estrutura e consequências conhecidas. O programa lista dez: Factory-Method, Command, Composite, Observer, Strategy, Abstract Factory, State, Adapter, Decorator e Singleton. Não os decores como definições: aprende a reconhecer o problema que cada um resolve, porque nos exercícios e nas provas a pergunta é sempre “que padrão aplicavas aqui”.

Os dez padrões em rascunho rápido

Strategy troca algoritmos em tempo de execução: o Monstro recebe um Comportamento diferente. Usa quando há várias formas de fazer a mesma coisa.

State troca comportamento conforme o estado interno: um monstro calmo foge, um monstro alerta persegue. Usa quando um objeto se comporta de forma muito diferente consoante o estado, em vez de um switch gigante.

Observer avisa interessados quando algo muda: a pontuação observa os monstros e atualiza-se quando um cai. Usa para dependências um para muitos sem acoplamento.

Command transforma um pedido num objeto: cada tecla vira um Comando com executar. Usa para desfazer, filas e menus.

Factory Method e Abstract Factory criam objetos sem expor a classe concreta. A diferença está no tamanho da encomenda: o Factory Method cria um objeto (uma fábrica de monstros que devolve o tipo certo para cada nível), enquanto a Abstract Factory cria famílias coerentes (uma fábrica de nível que devolve monstros, paredes e itens no mesmo estilo). Usa o primeiro quando a criação de um objeto tem lógica, o segundo quando vários objetos têm de combinar entre si.

Composite trata um grupo como um objeto: uma parede feita de segmentos desenha-se como uma peça. Usa para estruturas em árvore.

Decorator acrescenta comportamento por embrulho: um monstro com bónus de velocidade embrulha o monstro base. Usa para combinações que explodiriam em subclasses.

Adapter converte uma interface noutra: adapta a biblioteca de ecrã à interface Ecra do jogo. Usa para integrar código que não controlas.

Singleton garante uma só instância: o registo de recordes. Usa com moderação, porque estado global dificulta testes.

Exemplo: Strategy e State nos monstros

O movimento dos monstros usa Strategy, como na página de Java:

Comportamento zigzag = new AndarEmZigZag();
Comportamento perseguir = new PerseguirHeroi(heroi);
Monstro m1 = new Monstro(new Posicao(5, 5), zigzag);
Monstro m2 = new Monstro(new Posicao(8, 2), perseguir);

Quando o nível aperta, o estado do monstro muda a estratégia por dentro:

public class MonstroAlerta implements EstadoMonstro {
    public Comportamento comportamento(Heroi heroi) {
        return new PerseguirHeroi(heroi);
    }
}

O Monstro pede ao seu estado o comportamento atual. Calmo devolve passeio, alerta devolve perseguição:

O switch sobre o estado desapareceu, e um estado novo é uma classe nova. Troca a estratégia em tempo de execução e vê a diferença:

Trocar de estratégia a meio
interface Comportamento {
    String mover();
}

class AndarEmZigZag implements Comportamento {
    public String mover() { return "ziguezagueia"; }
}

class PerseguirHeroi implements Comportamento {
    public String mover() { return "persegue o heroi"; }
}

class Monstro {
    private Comportamento comportamento;

    Monstro(Comportamento comportamento) {
        this.comportamento = comportamento;
    }

    void setComportamento(Comportamento comportamento) {
        this.comportamento = comportamento;
    }

    String agir() {
        return comportamento.mover();
    }
}

public class Main {
    public static void main(String[] args) {
        Monstro monstro = new Monstro(new AndarEmZigZag());
        System.out.println("nivel facil: " + monstro.agir());
        monstro.setComportamento(new PerseguirHeroi());
        System.out.println("nivel dificil: " + monstro.agir());
    }
}
Dados de entrada

A saída mostra nivel facil: ziguezagueia e nivel dificil: persegue o heroi. Nenhuma classe do monstro foi editada: a variação vive nos objetos de comportamento.

Exemplo: Observer na pontuação

A pontuação observa cada monstro sem os monstros saberem dela:

monstro.adicionarObservador(pontuacao);
// ... quando o monstro cai:
observadores.forEach(o -> o.notificar(100));

Adicionar um contador de mortes ou um efeito sonoro é registar mais um observador:

Nenhum monstro muda, porque nenhum monstro conhece os observadores: é o desacoplamento que o padrão promete.

A miniatura vem do YouTube. O vídeo só carrega quando clicas. Abrir no YouTube

Ver o ficheiro no GitHub

À tua maneira

Escolhe como preferes ler.

Aparência
Ajustar cores e largura
Cor de destaque do tema FEUP
Tipo de letra

Álgebra, lógica e uma ideia de cada vez.

As tuas escolhas ficam guardadas neste navegador.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.