Prof. Fernando

01 / ENSINO · GUIA PRÁTICO

Conceitos de Orientação a Objetos

Abstração, encapsulamento, herança e polimorfismo, cada um com um exemplo curto em JavaScript.

Os exemplos usam a sintaxe de classes do JavaScript. Se ainda não conhece class, constructor e new, comece pelo guia JavaScript e Orientação a Objetos.

01 · INTRODUÇÃOO que é programar orientado a objetos

A Programação Orientada a Objetos (POO) organiza o código em torno de objetos: representações de coisas do problema que reúnem dados e comportamentos no mesmo lugar. Em um sistema escolar, por exemplo, teríamos objetos como alunos, turmas e disciplinas.

  • Classe — o molde que descreve como um tipo de objeto é. Ex.: Aluno.
  • Objeto (ou instância) — algo concreto criado a partir da classe. Ex.: a aluna Ana.
  • Atributos (ou propriedades) — os dados de cada objeto. Ex.: nome, matrícula, notas.
  • Métodos — as ações que o objeto sabe realizar. Ex.: calcular a média.
class Aluno {
  constructor(nome) {
    this.nome = nome;     // atributo
  }

  estudar() {             // método
    return `${this.nome} está estudando.`;
  }
}

const ana = new Aluno("Ana");  // objeto (instância)
console.log(ana.estudar());    // "Ana está estudando."

A POO costuma ser apresentada a partir de quatro pilares. Eles não são regras isoladas: funcionam juntos para deixar o código mais organizado, mais fácil de entender e de modificar.

02 · PILAR 1Abstração: ficar só com o que importa

Abstração é representar uma coisa do mundo real apenas pelas características que importam para o problema, ignorando o resto. Um carro tem milhares de peças, mas um sistema de estacionamento só precisa da placa, do modelo e do horário de entrada.

A abstração também vale para o uso: quem usa um objeto só precisa saber o que ele faz, não como faz. Você dirige sem saber como funciona a injeção eletrônica.

class Veiculo {
  constructor(placa, modelo) {
    this.placa = placa;
    this.modelo = modelo;
    this.entrada = new Date();
  }

  horasEstacionado() {
    const ms = new Date() - this.entrada;
    return Math.max(1, Math.ceil(ms / (1000 * 60 * 60))); // cobra no mínimo 1 hora
  }
}

const carro = new Veiculo("ABC1D23", "Fiat Uno");
console.log(carro.horasEstacionado()); // 1

A classe Veiculo não tem cor, potência do motor nem número de portas — para o estacionamento, esses dados não fazem diferença. E quem chama horasEstacionado() não precisa saber que o cálculo é feito em milissegundos.

Em resumo: abstrair é escolher o que entra no modelo e esconder os detalhes de como as coisas funcionam por dentro.

03 · PILAR 2Encapsulamento: proteger os dados do objeto

Encapsulamento é esconder os dados internos de um objeto e permitir que eles sejam alterados apenas por métodos do próprio objeto. Assim, o objeto controla as regras e ninguém consegue deixá-lo em um estado inválido.

Em JavaScript, um campo cujo nome começa com # é privado: só pode ser acessado de dentro da classe.

class ContaBancaria {
  #saldo = 0;

  constructor(titular) {
    this.titular = titular;
  }

  depositar(valor) {
    if (valor <= 0) {
      throw new Error("O depósito precisa ser positivo.");
    }
    this.#saldo += valor;
  }

  sacar(valor) {
    if (valor > this.#saldo) {
      throw new Error("Saldo insuficiente.");
    }
    this.#saldo -= valor;
  }

  get saldo() {
    return this.#saldo;
  }
}

const conta = new ContaBancaria("Ana");
conta.depositar(100);
conta.sacar(30);
console.log(conta.saldo); // 70

conta.saldo = 1000000;    // não altera nada: não existe setter
console.log(conta.saldo); // 70

// conta.#saldo = 1000000;
// SyntaxError: campos privados não podem ser acessados fora da classe

A única forma de mudar o saldo é por depositar() e sacar(), que verificam as regras. O getter saldo permite ler o valor, mas não alterá-lo diretamente.

Em resumo: encapsular é guardar os dados dentro do objeto e oferecer métodos seguros para usá-los.

04 · PILAR 3Herança: reaproveitar o que já existe

Herança permite criar uma classe nova a partir de outra já existente. A classe filha recebe todos os atributos e métodos da classe mãe e pode acrescentar os seus. A relação típica é "é um": um cachorro é um animal.

Em JavaScript, a herança é feita com extends. No construtor da filha, super() chama o construtor da mãe:

class Animal {
  constructor(nome) {
    this.nome = nome;
  }

  dormir() {
    return `${this.nome} está dormindo.`;
  }
}

class Cachorro extends Animal {
  constructor(nome, raca) {
    super(nome);        // chama o constructor de Animal
    this.raca = raca;
  }

  buscarBolinha() {
    return `${this.nome} trouxe a bolinha!`;
  }
}

const rex = new Cachorro("Rex", "Vira-lata");
console.log(rex.dormir());        // "Rex está dormindo." — herdado
console.log(rex.buscarBolinha()); // "Rex trouxe a bolinha!" — próprio
console.log(rex instanceof Animal); // true

Cachorro não precisou reescrever nome nem dormir(): herdou de Animal.

Cuidado: só use herança quando a relação "é um" for verdadeira. Um carro tem um motor, mas não é um motor — nesse caso, o motor deve ser um atributo do carro (composição), não uma classe mãe.

Em resumo: herdar é reaproveitar uma classe existente e especializá-la.

05 · PILAR 4Polimorfismo: a mesma mensagem, respostas diferentes

Polimorfismo (do grego: "muitas formas") é a capacidade de objetos de classes diferentes responderem à mesma chamada de método, cada um do seu jeito. O código que faz a chamada não precisa saber qual é o tipo exato do objeto.

Há duas formas clássicas de polimorfismo: a sobrescrita (em inglês, override) e a sobrecarga (em inglês, overload).

Sobrescrita (override)

Na sobrescrita, a classe filha redefine um método que herdou da mãe, mantendo o mesmo nome. Quando o método é chamado, vale a versão da classe do objeto. É a forma mais comum de polimorfismo em JavaScript.

class Animal {
  constructor(nome) {
    this.nome = nome;
  }

  emitirSom() {
    return `${this.nome} faz um som.`;
  }
}

class Cachorro extends Animal {
  emitirSom() {
    return `${this.nome} late: Au au!`;
  }
}

class Gato extends Animal {
  emitirSom() {
    return `${this.nome} mia: Miau!`;
  }
}

const bichos = [new Cachorro("Rex"), new Gato("Mimi"), new Animal("Nemo")];

for (const bicho of bichos) {
  console.log(bicho.emitirSom());
}
// "Rex late: Au au!"
// "Mimi mia: Miau!"
// "Nemo faz um som."

O laço chama emitirSom() em todos os itens sem perguntar se é cachorro ou gato. Se amanhã surgir uma classe Vaca, basta criá-la com seu próprio emitirSom(): o laço continua funcionando sem nenhuma alteração.

Sobrecarga (overload)

Na sobrecarga, a mesma classe tem várias versões de um método com o mesmo nome, que se diferenciam pela quantidade (ou pelo tipo) de argumentos. Linguagens como Java e C# permitem declarar cada versão separadamente.

JavaScript não tem sobrecarga nativa: se você declarar dois métodos com o mesmo nome, o último simplesmente substitui o primeiro.

class Exemplo {
  falar(nome) {
    return `Olá, ${nome}!`;
  }

  falar(nome, sobrenome) {   // substitui o método acima
    return `Olá, ${nome} ${sobrenome}!`;
  }
}

console.log(new Exemplo().falar("Ana")); // "Olá, Ana undefined!"

Para obter o mesmo efeito, escrevemos um único método que verifica quantos argumentos recebeu e muda o comportamento de acordo. O parâmetro rest (...medidas) junta todos os argumentos em um array, e medidas.length diz quantos foram passados:

class Geometria {
  area(...medidas) {
    switch (medidas.length) {
      case 1: {                          // 1 argumento: quadrado
        const [lado] = medidas;
        return lado * lado;
      }
      case 2: {                          // 2 argumentos: retângulo
        const [base, altura] = medidas;
        return base * altura;
      }
      case 3: {                          // 3 argumentos: triângulo (fórmula de Herão)
        const [a, b, c] = medidas;
        const s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
      }
      default:
        throw new Error("Informe 1, 2 ou 3 medidas.");
    }
  }
}

const geo = new Geometria();
console.log(geo.area(4));        // 16 — quadrado de lado 4
console.log(geo.area(3, 5));     // 15 — retângulo 3 × 5
console.log(geo.area(3, 4, 5));  // 6  — triângulo de lados 3, 4 e 5

Quem chama area() usa sempre o mesmo nome; o método decide o que fazer pela quantidade de argumentos. Você já usa esse padrão em funções nativas: new Date() sem argumentos devolve a data atual, new Date(2026, 9, 5) monta uma data específica, e Math.max() aceita quantos números você quiser.

Em resumo: polimorfismo é tratar objetos diferentes da mesma forma e deixar cada um decidir como responder.

06 · RESUMOOs quatro pilares juntos

Um exemplo pequeno que usa os quatro pilares ao mesmo tempo — formas geométricas e suas áreas:

class Forma {                          // abstração: só o que importa
  #nome;                               // encapsulamento: campo privado

  constructor(nome) {
    this.#nome = nome;
  }

  get nome() {
    return this.#nome;
  }

  area() {
    return 0;
  }

  descrever() {
    return `${this.nome}: área ${this.area().toFixed(2)}`;
  }
}

class Retangulo extends Forma {        // herança
  constructor(base, altura) {
    super("Retângulo");
    this.base = base;
    this.altura = altura;
  }

  area() {                             // polimorfismo: sobrescrita (override)
    return this.base * this.altura;
  }
}

class Circulo extends Forma {          // herança
  constructor(raio) {
    super("Círculo");
    this.raio = raio;
  }

  area() {                             // polimorfismo: sobrescrita (override)
    return Math.PI * this.raio ** 2;
  }
}

const formas = [new Retangulo(3, 4), new Circulo(2)];
formas.forEach(forma => console.log(forma.descrever()));
// "Retângulo: área 12.00"
// "Círculo: área 12.57"

Pilar → pergunta que ele responde

  • Abstração — o que é essencial para representar isso?
  • Encapsulamento — quem pode mexer nesses dados?
  • Herança — essa classe é um tipo especial de outra?
  • Polimorfismo — objetos diferentes podem responder à mesma chamada?

No exemplo acima

  • Forma guarda só nome e área.
  • #nome só pode ser lido, nunca trocado de fora.
  • Retangulo e Circulo herdam descrever().
  • Cada forma sobrescreve (override) a própria area().

← Voltar para Ensino