🎓 Aulas de Programação Orientada a Objetos

Universo DC - Aprenda POO com exemplos práticos

Objetivo da Aula

Entender o conceito de Classes Abstratas e quando utilizá-las, usando o universo DC como exemplo.

Aula 03: Classes Abstratas


Conceito de Classe Abstrata

O que é uma Classe Abstrata?

Uma classe abstrata é uma classe que não pode ser instanciada diretamente. Ela serve como base para outras classes e pode conter métodos abstratos (sem implementação) que devem ser implementados pelas classes filhas.

Analogia: Uma classe abstrata é como um molde incompleto. Você não pode criar objetos diretamente dela, mas pode criar objetos das classes que a completam.

Características

Quando Usar Classes Abstratas?

Use quando:

Implementação: Personagem como Classe Abstrata

Versão Completa

// TÓPICO: Classe Abstrata
// 'abstract' indica que esta classe não pode ser instanciada diretamente
// Você não pode fazer: Personagem p = new Personagem(); ❌
// Mas pode fazer: Heroi h = new Heroi(); ✅ (se Heroi herda de Personagem)
public abstract class Personagem
{
    // Propriedades comuns a todos os personagens
    public string Nome { get; set; }
    public int Nivel { get; set; }

    // Construtor (mesmo sendo abstrata, pode ter construtor)
    public Personagem(string nome, int nivel)
    {
        Nome = nome;
        Nivel = nivel;
    }

    // TÓPICO: Método Concreto (com implementação)
    // Métodos concretos têm implementação e podem ser usados diretamente
    public void SubirNivel()
    {
        Nivel++;
        Console.WriteLine($"{Nome} subiu para o nível {Nivel}!");
    }

    // TÓPICO: Método Abstrato (sem implementação)
    // Métodos abstratos NÃO têm implementação
    // Classes filhas SÃO OBRIGADAS a implementar este método
    // Cada tipo de personagem tem sua forma única de usar poder
    public abstract void UsarPoder();

    // Outro método abstrato
    // Cada personagem se apresenta de forma diferente
    public abstract void Apresentar();

    // TÓPICO: Método Virtual (pode ser sobrescrito, mas tem implementação padrão)
    // Diferente do abstrato, o virtual tem implementação
    // Classes filhas podem usar a implementação padrão ou sobrescrever
    public virtual void Descansar()
    {
        Console.WriteLine($"{Nome} está descansando...");
    }
}

Versão Abreviada

// Versão mais concisa
public abstract class Personagem
{
    public string Nome { get; set; }
    public int Nivel { get; set; }

    public Personagem(string nome, int nivel)
    {
        Nome = nome;
        Nivel = nivel;
    }

    // Método concreto abreviado
    public void SubirNivel() =>
        Console.WriteLine($"{Nome} subiu para o nível {++Nivel}!");

    // Métodos abstratos (devem ser implementados nas classes filhas)
    public abstract void UsarPoder();
    public abstract void Apresentar();

    // Método virtual (implementação padrão, pode ser sobrescrito)
    public virtual void Descansar() =>
        Console.WriteLine($"{Nome} está descansando...");
}

Implementando Classes Filhas

Classe Heroi Implementando Métodos Abstratos

// TÓPICO: Implementação de Classe Abstrata
// Heroi DEVE implementar todos os métodos abstratos de Personagem
public class Heroi : Personagem
{
    public string Equipe { get; set; }
    public int VidasSalvas { get; set; }

    public Heroi(string nome, int nivel, string equipe) : base(nome, nivel)
    {
        Equipe = equipe;
        VidasSalvas = 0;
    }

    // TÓPICO: Implementação Obrigatória de Método Abstrato
    // DEVE implementar UsarPoder() (obrigatório, vem de Personagem)
    public override void UsarPoder()
    {
        Console.WriteLine($"{Nome} usa seu poder para proteger os inocentes!");
    }

    // TÓPICO: Implementação Obrigatória de Método Abstrato
    // DEVE implementar Apresentar() (obrigatório, vem de Personagem)
    public override void Apresentar()
    {
        Console.WriteLine($"Olá! Eu sou {Nome}, herói da {Equipe}, nível {Nivel}!");
        Console.WriteLine($"Já salvei {VidasSalvas} vidas!");
    }

    // TÓPICO: Sobrescrita Opcional de Método Virtual
    // PODE sobrescrever Descansar() (opcional, já tem implementação em Personagem)
    public override void Descansar()
    {
        Console.WriteLine($"{Nome} descansa na Batcaverna, recuperando forças...");
    }

    // Método específico de heróis
    public void SalvarVida()
    {
        VidasSalvas++;
        Console.WriteLine($"{Nome} salvou uma vida! Total: {VidasSalvas} vidas salvas.");
    }
}

Classe Vilao Implementando Métodos Abstratos

// TÓPICO: Implementação de Classe Abstrata
// Vilao também DEVE implementar todos os métodos abstratos
public class Vilao : Personagem
{
    public string Organizacao { get; set; }
    public int PlanosExecutados { get; set; }

    public Vilao(string nome, int nivel, string organizacao) : base(nome, nivel)
    {
        Organizacao = organizacao;
        PlanosExecutados = 0;
    }

    // TÓPICO: Implementação Obrigatória (diferente da de Heroi)
    // Cada classe implementa de forma diferente
    public override void UsarPoder()
    {
        Console.WriteLine($"{Nome} usa seu poder para causar caos e destruição!");
    }

    // TÓPICO: Implementação Obrigatória (diferente da de Heroi)
    public override void Apresentar()
    {
        Console.WriteLine($"Cuidado! Eu sou {Nome}, vilão da {Organizacao}, nível {Nivel}!");
        Console.WriteLine($"Já executei {PlanosExecutados} planos maléficos!");
    }

    // TÓPICO: Não Sobrescreve Descansar()
    // Usa a implementação padrão de Personagem
    // (Não precisa sobrescrever, mas pode se quiser)

    // Método específico de vilões
    public void ExecutarPlano()
    {
        PlanosExecutados++;
        Console.WriteLine($"{Nome} executou um plano maléfico! Total: {PlanosExecutados} planos.");
    }
}

Criando Objetos

// TÓPICO: Instanciação de Classes Derivadas
// Não podemos criar Personagem diretamente (é abstrata)
// Personagem p = new Personagem("Teste", 1); ❌ ERRO!

// Mas podemos criar objetos das classes filhas
Heroi batman = new Heroi("Batman", 85, "Liga da Justiça");
Heroi superman = new Heroi("Superman", 100, "Liga da Justiça");
Vilao coringa = new Vilao("Coringa", 80, "Legião do Mal");
Vilao lexLuthor = new Vilao("Lex Luthor", 75, "Legião do Mal");

// Usando métodos abstratos (cada um implementa diferente)
batman.UsarPoder();        // "Batman usa seu poder para proteger os inocentes!"
coringa.UsarPoder();       // "Coringa usa seu poder para causar caos e destruição!"

batman.Apresentar();       // Apresentação de herói
coringa.Apresentar();       // Apresentação de vilão

// Usando método concreto (herdado, funciona igual para todos)
batman.SubirNivel();       // Funciona porque é método concreto
coringa.SubirNivel();      // Funciona porque é método concreto

// Usando método virtual
batman.Descansar();        // Versão sobrescrita (Batcaverna)
coringa.Descansar();       // Versão padrão (da classe base)

Polimorfismo com Classes Abstratas

// TÓPICO: Polimorfismo com Classes Abstratas
// Podemos tratar objetos de classes derivadas como Personagem
List<Personagem> personagens = new List<Personagem>();

personagens.Add(new Heroi("Batman", 85, "Liga da Justiça"));
personagens.Add(new Heroi("Superman", 100, "Liga da Justiça"));
personagens.Add(new Vilao("Coringa", 80, "Legião do Mal"));
personagens.Add(new Vilao("Lex Luthor", 75, "Legião do Mal"));

// Iterando e chamando métodos abstratos
foreach (Personagem personagem in personagens)
{
    // Cada objeto chama sua própria implementação
    personagem.UsarPoder();    // Polimorfismo!
    personagem.Apresentar();    // Polimorfismo!
    Console.WriteLine();       // Linha em branco
}

Comparação: Abstract vs Virtual vs Concreto

TipoImplementaçãoObrigatório Implementar?Pode Instanciar Classe?
AbstractSem implementação✅ SIM (obrigatório)❌ NÃO
VirtualCom implementação❌ NÃO (opcional)✅ SIM (se não for abstract)
ConcretoCom implementação❌ NÃO✅ SIM

Resumo

Próxima Aula

Na próxima aula, veremos Interfaces e como criar contratos que classes devem seguir.