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. -
virtualna 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).
Top comments (0)