Programação II – ECO Aulas Revisão

Revisão de Orientação a Objetos com JavaScript

Nesta unidade revisamos os principais conceitos de programação orientada a objetos, agora focados em JavaScript e no contexto de desenvolvimento web.

Introdução

A Programação Orientada a Objetos (POO) organiza o código em torno de objetos, que representam entidades com dados e comportamentos. Nesta página, vamos revisar os conceitos fundamentais de POO usando JavaScript, preparando a base para o desenvolvimento web que veremos ao longo da disciplina.

Configuração do ambiente para os exemplos

Você pode executar os exemplos desta unidade diretamente no navegador (recomendado para começar) ou com Node.js.

Opção 1 – Navegador

  1. Crie uma pasta em seu computador, por exemplo prog2-revisao-oo.
  2. Dentro da pasta, crie um arquivo chamado index.html com o conteúdo abaixo.
<!DOCTYPE html>
<html lang="pt-BR">
  <head>
    <meta charset="UTF-8" />
    <title>Revisão OO - Programação II</title>
  </head>
  <body>
    <h1>Revisão de Orientação a Objetos em JavaScript</h1>
    <p>Abra o console do navegador para ver a saída dos exemplos.</p>

    <script src="revisao-oo.js"></script>
  </body>
</html>
  1. Crie um arquivo chamado revisao-oo.js na mesma pasta. É nele que você vai colar os exemplos de código desta página.
  2. Abra o arquivo index.html no navegador (clique duas vezes ou use “Abrir com > Navegador”).
  3. Pressione F12 e selecione a aba Console para ver a saída dos códigos.

Entendendo o arquivo HTML base

O bloco abaixo é uma explicação “anotada” do arquivo HTML anterior. Leia junto com o exemplo para entender o papel de cada linha.

<!DOCTYPE html>
Indica ao navegador que o documento usa HTML5, permitindo uma renderização mais padronizada.
<html lang="pt-BR">
Abre a tag raiz do documento HTML e informa que o idioma principal da página é português do Brasil, o que ajuda na acessibilidade e em mecanismos de busca.
<head> ... </head>
Define a área de metadados da página, onde ficam informações que não aparecem diretamente na tela (configuração de caracteres, título, links de estilo etc.).
<meta charset="UTF-8" />
Configura a codificação de caracteres para UTF‑8, garantindo que acentos e demais caracteres especiais sejam exibidos corretamente.
<title>Revisão OO - Programação II</title>
Define o texto que aparece na aba do navegador e é usado por favoritos e motores de busca como o título da página.
<body> ... </body>
Delimita o corpo do documento, isto é, todo o conteúdo visível para quem acessa a página (textos, imagens, botões, scripts em execução etc.).
<h1>Revisão de Orientação a Objetos em JavaScript</h1>
Cria o título principal da página. Por padrão é exibido em destaque e marca o assunto mais importante do documento.
<p>Abra o console do navegador para ver a saída dos exemplos.</p>
Exibe um parágrafo com instruções para o usuário, orientando que ele abra o console do navegador para acompanhar a saída do código JavaScript.
<script src="revisao-oo.js"></script>
Importa e executa o arquivo externo revisao-oo.js. Todo o JavaScript escrito nesse arquivo é carregado quando a página abre e sua saída aparece no console.
</html>
Fecha o documento HTML, indicando ao navegador que não há mais conteúdo a ser processado.

Opção 2 – Node.js (para quem já instalou)

  1. Instale o Node.js a partir do site oficial, se ainda não tiver.
  2. Na mesma pasta, mantenha o arquivo revisao-oo.js com os exemplos.
  3. Abra um terminal na pasta e execute:
    node revisao-oo.js
Em sala, vamos priorizar o uso do navegador, pois é o mesmo ambiente em que as aplicações web dos próximos módulos irão rodar.

Objetivos da unidade

1. Conceitos básicos de POO em JavaScript

1.1 Classe e objeto

Em POO, a classe é um molde que descreve quais dados e comportamentos um tipo de objeto terá, enquanto o objeto é uma instância concreta dessa classe.

// Exemplo 1 – Classe Produto

class Produto {
  constructor(nome, preco) {
    if (!nome || typeof nome !== 'string') {
      throw new Error('Nome do produto deve ser uma string não vazia.');
    }

    if (typeof preco !== 'number' || preco <= 0) {
      throw new Error('Preço deve ser um número positivo.');
    }

    this.nome = nome;
    this.preco = preco;
  }

  aplicarDesconto(percentual) {
    if (typeof percentual !== 'number' || percentual <= 0 || percentual >= 100) {
      throw new Error('Percentual deve ser um número entre 0 e 100.');
    }

    const fatorDesconto = 1 - percentual / 100;
    this.preco *= fatorDesconto;
  }

  descrever() {
    const precoFormatado = this.preco.toFixed(2);
    return `${this.nome} - R$ ${precoFormatado}`;
  }
}

// Uso da classe Produto
const mouse = new Produto('Mouse sem fio', 80);
const teclado = new Produto('Teclado mecânico', 350);

mouse.aplicarDesconto(10);

console.log('Produtos cadastrados:');
console.log(mouse.descrever());
console.log(teclado.descrever());

Entendendo o Exemplo 1 – Classe Produto

Este exemplo mostra como modelar um produto usando uma classe em JavaScript, incluindo validação de dados e um método para aplicar desconto.

class Produto { ... }
Define uma nova classe chamada Produto, que servirá como “molde” para criar objetos que representam produtos à venda.
constructor(nome, preco) { ... }
O construtor é chamado quando criamos um novo produto. Ele recebe o nome e o preco como parâmetros, valida esses dados (não aceita nome vazio nem preço menor ou igual a zero) e, se estiver tudo certo, guarda esses valores dentro do objeto.
aplicarDesconto(percentual) { ... }
Este método recebe um percentual de desconto e também faz validação (entre 0 e 100). Em seguida, calcula um fator de desconto e atualiza o atributo preco multiplicando pelo fator. Assim, o próprio objeto “sabe” como aplicar descontos sobre si mesmo.
descrever() { ... }
Gera uma string amigável com o nome do produto e o preço formatado com duas casas decimais. Essa é uma forma de centralizar a formatação em um único lugar, facilitando o reuso em diferentes partes do sistema.
const mouse = new Produto(...) / const teclado = new Produto(...)
Aqui criamos dois objetos concretos (instâncias) da classe Produto: um mouse e um teclado, cada um com seu próprio preço.
mouse.aplicarDesconto(10);
Chamamos o método da instância mouse para aplicar um desconto de 10%. Apenas o preço do mouse é atualizado; o teclado continua com o valor original.
console.log(...)
As chamadas ao console.log servem para exibir no console o texto “Produtos cadastrados” e, em seguida, a descrição de cada produto retornada pelo método descrever(). É assim que verificamos, na prática, se o código está se comportando como esperado.

1.2 Atributos, métodos e responsabilidade

Uma boa prática é manter cada classe com uma responsabilidade clara. No exemplo abaixo, a classe Estudante é responsável por representar o estado acadêmico de um estudante em um curso.

// Exemplo 2 – Classe Estudante

class Estudante {
  constructor(nome, matricula, curso) {
    if (!nome || !matricula || !curso) {
      throw new Error('Estudante precisa de nome, matrícula e curso.');
    }

    this.nome = nome;
    this.matricula = matricula;
    this.curso = curso;
    this.status = 'Em andamento';
  }

  aprovar() {
    this.status = 'Aprovado';
  }

  reprovar() {
    this.status = 'Reprovado';
  }

  obterResumo() {
    return `${this.nome} (${this.matricula}) - ${this.curso} - ${this.status}`;
  }
}

const maria = new Estudante('Maria', '2026001', 'Engenharia de computação');
console.log(maria.obterResumo());
maria.aprovar();
console.log(maria.obterResumo());

2. Pilares da Programação Orientada a Objetos

2.1 Abstração

Abstrair significa representar apenas o que é relevante de um objeto para o contexto do sistema, ignorando detalhes desnecessários.

// Exemplo 3 – Classe Professor

class Professor {
  constructor(nome, cpf, area) {
    if (!nome || !cpf || !area) {
      throw new Error('Professor precisa de nome, CPF e área.');
    }

    this.nome = nome;
    this.cpf = cpf;
    this.area = area;
  }

  resumo() {
    return `${this.nome} (${this.area})`;
  }
}

const profWilcilene = new Professor('Wilcilene Maria Kowal', '123.456.789-00', 'Programação II, que não é Web');
console.log(profWilcilene.resumo());

2.2 Encapsulamento

Encapsulamento significa proteger os dados de acesso direto indevido, expondo apenas o que realmente precisa ser público. Em JavaScript podemos usar campos privados com #.

// Exemplo 4 – ContaBancaria com atributo privado

class ContaBancaria {
  #saldo;

  constructor(titular) {
    if (!titular) {
      throw new Error('Conta precisa de um titular.');
    }

    this.titular = titular;
    this.#saldo = 0;
  }

  depositar(valor) {
    if (typeof valor !== 'number' || valor <= 0) {
      throw new Error('Depósito deve ser um número positivo.');
    }

    this.#saldo += valor;
  }

  sacar(valor) {
    if (typeof valor !== 'number' || valor <= 0) {
      throw new Error('Saque deve ser um número positivo.');
    }

    if (valor > this.#saldo) {
      throw new Error('Saldo insuficiente para saque.');
    }

    this.#saldo -= valor;
  }

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

const contaGilsilene = new ContaBancaria('Gilsilene');
contaGilsilene.depositar(500);
contaGilsilene.sacar(150);

console.log(`Saldo da conta de ${contaGilsilene.titular}: R$ ${contaGilsilene.obterSaldo()}`);
// contaGilsilene.#saldo = 9999; // Erro: campo privado

2.3 Herança

Herança permite reaproveitar código criando classes mais específicas a partir de uma mais geral. Usamos extends e super() em JavaScript.

// Exemplo 5 – Usuário e Admin

class Usuario {
  constructor(nome, email) {
    if (!nome || !email) {
      throw new Error('Usuário precisa de nome e email.');
    }

    this.nome = nome;
    this.email = email;
  }

  apresentar() {
    console.log(`Olá, eu sou ${this.nome}.`);
  }
}

class Admin extends Usuario {
  constructor(nome, email) {
    super(nome, email);
    this.perfil = 'ADMIN';
  }

  bloquearUsuario(usuario) {
    if (!(usuario instanceof Usuario)) {
      throw new Error('É possível bloquear apenas um objeto do tipo Usuario.');
    }

    console.log(`Usuário ${usuario.nome} foi bloqueado por ${this.nome}.`);
  }
}

const usuarioJoao = new Usuario('João', 'joao@exemplo.com');
const adminMaria = new Admin('Maria', 'maria@exemplo.com');

usuarioJoao.apresentar();
adminMaria.apresentar();
adminMaria.bloquearUsuario(usuarioJoao);

2.4 Polimorfismo

Polimorfismo é a capacidade de um mesmo método ter comportamentos diferentes, dependendo da classe concreta do objeto.

// Exemplo 6 – Formas geométricas

class Forma {
  calcularArea() {
    throw new Error('Método calcularArea() deve ser implementado pela subclasse.');
  }
}

class Retangulo extends Forma {
  constructor(largura, altura) {
    super();

    if (largura <= 0 || altura <= 0) {
      throw new Error('Largura e altura devem ser positivas.');
    }

    this.largura = largura;
    this.altura = altura;
  }

  calcularArea() {
    return this.largura * this.altura;
  }
}

class Circulo extends Forma {
  constructor(raio) {
    super();

    if (raio <= 0) {
      throw new Error('Raio deve ser positivo.');
    }

    this.raio = raio;
  }

  calcularArea() {
    return Math.PI * this.raio ** 2;
  }
}

function imprimirArea(forma) {
  if (!(forma instanceof Forma)) {
    throw new Error('O parâmetro deve ser uma instância de Forma.');
  }

  console.log('Área:', forma.calcularArea().toFixed(2));
}

imprimirArea(new Retangulo(3, 4));
imprimirArea(new Circulo(2));

Áudio criado pelo NotebokLM após análise desta página

Use o player abaixo para ouvir a explicação gravada deste tema

3. Para memorizar!

Atividade 1 – Modelando uma turma

Modele as classes abaixo em JavaScript, seguindo as práticas estudadas nos exemplos (validação de dados, nomes claros, responsabilidade única).

  • Curso: possui nome e carga horária.
  • Turma: está ligada a um curso e contém uma lista de estudantes (Estudante).

Regras sugeridas:

  • Não permitir estudantes duplicados na turma (mesma matrícula).
  • Garantir que toda turma esteja ligada a um curso válido.
Atividade 2 – Encapsulando um carrinho de compras

Implemente uma classe CarrinhoDeCompras com:

  • Lista interna de itens (cada item com descricao, preco, quantidade).
  • Métodos: adicionarItem, removerItem, calcularTotal, listarItens.

Regras:

  • Não aceitar quantidade zero ou negativa.
  • Esconder a lista interna (encapsulamento) e expor apenas métodos de acesso.
Atividade 3 – Herança e polimorfismo com veículos

Implemente:

  • Veiculo com marca, modelo, ano e método descricao().
  • Carro e Moto, que herdam de Veiculo e sobrescrevem descricao().

Crie uma função imprimirDescricao(veiculo) que recebe qualquer Veiculo (ou subclasse) e chama descricao(), demonstrando polimorfismo.

Atividade 4 – Biblioteca

Crie as classes Livro, UsuarioBiblioteca e, se achar necessário, uma classe para controlar empréstimos.

Regras sugeridas:

  • Use encapsulamento para proteger a lista interna de livros emprestados.
  • Impeça que o mesmo livro seja emprestado duas vezes para o mesmo usuário.
  • Inclua um método para listar todos os empréstimos ativos.
Atividade 5 – Sistema de pagamentos

Crie uma hierarquia de Pagamento com subclasses para PagamentoCartao, PagamentoPix e PagamentoBoleto.

Orientações:

  • Defina na classe base um método processar() que é sobrescrito em cada subclasse com a lógica específica de mensagem ou validação.
  • Implemente uma função pagar(pagamento) que recebe qualquer objeto da hierarquia e chama processar(), explorando o polimorfismo.

Roteiro de estudo para quem faltou

Assista à explicação dessa aula gerada pelo NotebookLM.