programandoai

Classe, objeto literal ou closure? E getters/setters

Agora que você conhece as três formas de organizar dados e comportamento, vem a pergunta prática: qual usar em cada situação? E como controlar o acesso a um campo sem expor tudo cru?

Estudar esta lição interativa →

Você agora tem três ferramentas para organizar dados e comportamento: o objeto literal (Módulo 6), a closure (início deste módulo) e a classe (lição passada). Elas se sobrepõem, e é natural ficar em dúvida sobre qual usar. Não existe uma regra rígida, mas existe um jeito prático de decidir, olhando para duas perguntas: "vou precisar de várias instâncias parecidas?" e "preciso de algum dado privado, inacessível de fora?".

SituaçãoMelhor escolhaPor quê
Um dado único, sem repetição (configuração de um app, estado de uma tela)Objeto literalSimples, direto, sem o peso de criar uma classe para uma coisa só
Poucas variáveis privadas e um punhado de funções, sem necessidade de herançaClosureEsconde o estado de verdade (ninguém de fora acessa a variável), sem sintaxe extra
Muitas instâncias do mesmo tipo, possivelmente com herançaClasseCompartilha métodos de forma eficiente (via protótipo), deixa clara a relação entre os tipos e, com campos #privados, também esconde o estado de verdade

Um guia prático, não uma lei: em projetos reais, as três formas convivem, cada uma no lugar onde encaixa melhor.

Uma última ferramenta das classes fecha bem essa comparação: getters e setters. Eles fazem um método se comportar como uma propriedade comum, mas executando código por trás. Um get roda quando alguém lê o campo (sem parênteses, como produto.preco, não produto.preco()); um set roda quando alguém escreve nele (produto.preco = 50). E é exatamente aí que mora a maior utilidade deles: o set é o lugar perfeito para validar um valor antes de aceitá-lo, algo que uma propriedade comum não permite fazer.

class Produto {
  #preco; // campo PRIVADO: só o código de dentro da classe enxerga

  constructor(nome, preco) {
    this.nome = nome;
    this.#preco = preco;
  }
  get preco() {
    return this.#preco;
  }
  set preco(valor) {
    if (valor < 0) {
      throw new Error("Preço não pode ser negativo");
    }
    this.#preco = valor;
  }
}

const p = new Produto("Caneta", 3);
console.log(p.preco); // 3 (lê como propriedade comum, mas chama o getter)

p.preco = 5; // escreve como propriedade comum, mas chama o setter
console.log(p.preco); // 5

try {
  p.preco = -10; // o setter recusa
} catch (e) {
  console.log("Erro:", e.message);
}
#preco: privado de verdade (e o antigo _preco)

No exemplo, o valor mora no campo #preco. O # na frente do nome cria um campo privado: só o código de dentro da classe consegue ler ou escrever; escrever p.#preco do lado de fora nem chega a rodar, o JavaScript acusa erro de sintaxe. Por isso o único caminho para mudar o preço é o set, que valida. Em códigos mais antigos você vai encontrar _preco (com sublinhado): é só uma convenção para dizer "não mexa aqui", mas nada impede quem quiser de ler ou alterar p._preco direto, passando por cima do setter. Prefira o # em código novo.

Não exagere

Getter e setter são ótimos quando existe uma regra de verdade por trás (validar, calcular algo derivado, disparar um efeito colateral). Se o campo é só um valor simples, sem nenhuma lógica de leitura ou escrita, uma propriedade comum (this.nome = nome) é mais direta e mais fácil de ler. Adicionar getter/setter "porque parece mais profissional" só adiciona código sem benefício real.

// Sua vez! Complete o getter "fahrenheit".
class Termometro {
  #celsius; // privado: só a classe mexe

  constructor(celsius) {
    this.#celsius = celsius;
  }
  get celsius() {
    return this.#celsius;
  }
  get fahrenheit() {
    // calcule e devolva: celsius * 9/5 + 32
  }
}

const t = new Termometro(100);
console.log(t.fahrenheit); // deve dar 212

Complete o getter fahrenheit e clique em Rodar.

← Voltar ao índice do curso