javascript-metodos-y-variables-privados

Métodos y variables privados en JavaScript

La encapsulación es un concepto que proviene de la programación orientada a objetos (POO) que consiste en ocultar y proteger los detalles internos de una clase

La encapsulación tiene dos aspectos principales:

  • Ocultar el estado interno de los objetos: Los objetos pueden tener propiedades privadas, que no son accesibles directamente desde fuera de la clase
  • Proveer una interfaz pública: La clase puede exponer métodos públicos que permiten interactuar con sus propiedades de manera controlada

Es decir, se trata de proteger el acceso directo a los datos y exponer solo lo necesario para que otras partes del programa interactúen con esa clase.

Al ocultar el estado interno de los objetos y proporcionar una interfaz pública controlada, aseguramos que nuestros objetos sean más robustos, fáciles de mantener y menos propensos a errores.

Propiedades privadas

En JavaScript ES2022 introdujo la sintaxis de campos privados. Para ello se antepone el nombre de la propiedad con el prefijo #.

Las propiedades que utilizan este prefijo son accesibles solo dentro de la clase en la que están definidas (no pueden ser accedidas ni modificadas desde fuera de la clase).

class Persona {
    #nombre;

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

    obtenerNombre() {
        return this.#nombre;
    }
}

const persona1 = new Persona("Juan");
// Acceso correcto a través de un método público
console.log(persona1.obtenerNombre()); // "Juan"

// Esto causará un error: "Cannot read private member"
console.log(persona1.#nombre); // Error

En este ejemplo:

  • #nombre es una propiedad privada. Solo puede ser accedida dentro de la clase Persona.
  • obtenerNombre() es un método público que permite acceder al valor de #nombre.

Métodos privados

Los métodos también pueden ser privados. Al igual que las propiedades, se definen con el prefijo #:

class Usuario {
    #claveSecreta;

    constructor(clave) {
        this.#claveSecreta = clave;
    }

    #mostrarClave() {
        console.log(`La clave secreta es: ${this.#claveSecreta}`);
    }

    autenticar() {
        this.#mostrarClave(); // Llamada válida desde dentro de la clase
    }
}

const usuario1 = new Usuario("12345");
usuario1.autenticar(); // La clave secreta es: 12345
// Esto causará un error: "Cannot read private member"
// usuario1.#mostrarClave(); // Error

En este ejemplo, #mostrarClave() es un método privado. Solo puede ser llamado desde dentro de la clase, no desde fuera.

Métodos getters y setters

La encapsulación encaja muy bien con el uso de métodos getter y setter.

class Persona {
    #edad;

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

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

    set edad(nuevaEdad) {
        if (nuevaEdad > 0) {
            this.#edad = nuevaEdad;
        } else {
            console.log("La edad debe ser positiva.");
        }
    }
}

const persona2 = new Persona(25);
console.log(persona2.edad); // 25
persona2.edad = 30;         // Modifica la edad
console.log(persona2.edad); // 30
persona2.edad = -5;         // La edad debe ser positiva.

En este ejemplo:

  • El getter edad permite acceder al valor de la propiedad privada #edad.
  • El setter edad permite modificar el valor de #edad solo si es un número positivo.

Ejemplo simple de encapsulación

Vamos a ver un ejemplo básico de cómo se puede encapsular un dato dentro de una clase:

class CuentaBancaria {
    // Propiedad privada (accedida solo desde la clase)
    #balance;

    constructor(inicial) {
        this.#balance = inicial;
    }

    // Método público para obtener el balance
    obtenerBalance() {
        return this.#balance;
    }

    // Método público para depositar dinero
    depositar(cantidad) {
        if (cantidad > 0) {
            this.#balance += cantidad;
        } else {
            console.log("El cantidad debe ser positivo.");
        }
    }

    // Método público para retirar dinero
    retirar(cantidad) {
        if (cantidad <= this.#balance) {
            this.#balance -= cantidad;
        } else {
            console.log("Fondos insuficientes.");
        }
    }
}

const cuenta1 = new CuentaBancaria(1000);
console.log(cuenta1.obtenerBalance());  // 1000
cuenta1.depositar(500);
console.log(cuenta1.obtenerBalance());  // 1500
cuenta1.retirar(200);
console.log(cuenta1.obtenerBalance());  // 1300

En este ejemplo:

  • La propiedad #balance es privada y no puede ser accedida directamente desde fuera de la clase.
  • Los métodos depositar(), retirar() y obtenerBalance() permiten interactuar con el balance de la cuenta de forma controlada.

También podíamos haber usado métodos setter y getter, pero así vemos otra forma con métodos normales