POO em JavaScript

JavaScript Avançado

POO (parte 3 - Encapsulamento)

Encapsulamento em JavaScript

O encapsulamento é um dos pilares fundamentais da Programação Orientada a Objetos que consiste em esconder os detalhes internos de implementação de um objeto, expondo apenas uma interface pública para interação.

Como implementar encapsulamento em JavaScript

JavaScript não possui modificadores de acesso como private, protected e public como em outras linguagens, mas podemos simular encapsulamento de várias formas:

1. Convenção com underscore (_)


class ContaBancaria {
  constructor(saldo) {
    this._saldo = saldo; // _ indica que é "privado" (convenção)
  }

  depositar(valor) {
    this._saldo += valor;
  }

  getSaldo() {
    return this._saldo;
  }
}

const conta = new ContaBancaria(1000);
console.log(conta._saldo); // Ainda acessível (apenas convenção)
        

2. Usando Symbols


const saldoSymbol = Symbol('saldo');

class ContaBancaria {
  constructor(saldo) {
    this[saldoSymbol] = saldo;
  }

  depositar(valor) {
    this[saldoSymbol] += valor;
  }

  getSaldo() {
    return this[saldoSymbol];
  }
}

const conta = new ContaBancaria(1000);
console.log(conta[saldoSymbol]); // Acessível se tiver a referência do Symbol
        

3. Usando WeakMaps (mais privacidade)


const privado = new WeakMap();

class ContaBancaria {
  constructor(saldo) {
    privado.set(this, { saldo });
  }

  depositar(valor) {
    const dados = privado.get(this);
    dados.saldo += valor;
  }

  getSaldo() {
    return privado.get(this).saldo;
  }
}

const conta = new ContaBancaria(1000);
console.log(conta.saldo); // undefined (realmente privado)
        

4. Campos privados em classes (ES2022)


class ContaBancaria {
  #saldo; // Campo privado

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

  depositar(valor) {
    this.#saldo += valor;
  }

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

const conta = new ContaBancaria(1000);
console.log(conta.#saldo); // Erro de sintaxe (realmente privado)
console.log(conta.saldo); // Acessível via getter
        

Vantagens do Encapsulamento

Exemplo Completo com Encapsulamento


class Produto {
  #nome;
  #preco;
  #desconto;

  constructor(nome, preco) {
    this.#nome = nome;
    this.#preco = preco;
    this.#desconto = 0;
  }

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

  get preco() {
    return this.#preco - (this.#preco * this.#desconto);
  }

  set desconto(novoDesconto) {
    if (novoDesconto >= 0 && novoDesconto <= 0.5) {
      this.#desconto = novoDesconto;
    } else {
      throw new Error('Desconto deve estar entre 0 e 50%');
    }
  }

  detalhes() {
    return `${this.#nome} - R$ ${this.preco.toFixed(2)} ${this.#desconto > 0 ? `(${this.#desconto * 100}% off)` : ''}`;
  }
}

const notebook = new Produto('Notebook', 3000);
notebook.desconto = 0.2;
console.log(notebook.detalhes()); // "Notebook - R$ 2400.00 (20% off)"