POO: Herança
# Introdução
Uma classe (subclasse/derivada) reaproveita estrutura e comportamento de outra (superclasse/base), podendo especializar o que for necessário.
class Funcionario
{
public string Nome { get; set; }
protected decimal SalarioBase;
public Funcionario(string nome, decimal salarioBase)
{
Nome = nome;
SalarioBase = salarioBase;
}
public virtual decimal CalcularSalario() => SalarioBase;
}
class Gerente : Funcionario
{
private decimal _bonus;
public Gerente(string nome, decimal salarioBase, decimal bonus)
: base(nome, salarioBase)
{
_bonus = bonus;
}
public override decimal CalcularSalario() => base.CalcularSalario() + _bonus;
}
Repare em três mecanismos importantes:
* `: base(nome, salarioBase)` — chama o construtor da classe pai.
* `virtual` na base — sinaliza "isso pode ser reescrito".
* `override` + `base.CalcularSalario()` — reescreve mas ainda reaproveita a lógica original.
# **Tipos de relação que herança modela**
Regra de ouro é o teste **"é um"** (is-a): `Gerente` **é um** `Funcionario`. Se a relação real é "**tem um** " (has-a), a resposta certa é composição, não herança — `Carro` **tem um** `Motor`, não deveria herdar de `Motor`. O livro “Head First Design Pattern”, aborda de forma eficiente sobre preferir composições ao invés de herança.
**`sealed`** trava a herança:
sealed class RelatorioFinal : Funcionario /* ninguém pode herdar disso */
Útil quando você quer garantir que o comportamento não será alterado por subclasses além de ser bom pra segurança e pra o compilador otimizar chamadas (sem precisar checar override em runtime).
**A armadilha da herança profunda:** cada nível adiciona acoplamento. Se `Funcionario → Gerente → GerenteRegional → DiretorRegional`, uma mudança lá em cima em `Funcionario` pode quebrar comportamento três níveis abaixo de um jeito difícil de rastrear. Regra prática: se você passar de 2 níveis de herança, pare e pergunte se composição resolveria melhor (voltamos nisso na conversa anterior, com o exemplo do `IComportamentoVoo`).
**C# não tem herança múltipla de classes** (só de interfaces) — justamente pra evitar o "problema do diamante": se `C` herdasse de `A` e `B`, e ambos tivessem um método `Fazer()`, qual implementação `C` usaria? C# resolve isso simplesmente proibindo.
# Exemplo de jogo
Temos uma classe `Personagem` e agora criamos tipos especializados que reaproveitam tudo que `Personagem` já tem:
class Personagem
{
// ...
public void Atacar(Personagem alvo)
{
int dano = CalcularDano(); // fórmula escondida
alvo.TomarDano(dano);
Console.WriteLine($"{Nome} atacou {alvo.Nome} causando {dano} de dano!");
}
private int CalcularDano()
{
bool critico = new Random().Next(100) < 15; // 15% de chance
int danoBase = 20;
return critico ? danoBase * 2 : danoBase;
}
}
class Guerreiro : Personagem
{
public int Armadura { get; set; }
public Guerreiro(string nome) : base(nome, vidaMaxima: 150) { Armadura = 20; }
}
class Mago : Personagem
{
public int Mana { get; private set; } = 100;
public Mago(string nome) : base(nome, vidaMaxima: 80) { }
}
class Arqueiro : Personagem
{
public Arqueiro(string nome) : base(nome, vidaMaxima: 100) { }
}
`Guerreiro`, `Mago` e `Arqueiro` ganham `Nome`, `Vida`, `Atacar()`, `TomarDano()` e `Curar()` de graça — não precisaram reescrever nada disso. Cada um só adiciona o que é específico dele (`Armadura`, `Mana`).