Propiedades, métodos privados y protegidos en JavaScript
Uno de los principios fundamentales en la programación orientada a objetos es la separación clara entre la interfaz pública y la interna.
Este enfoque es esencial al desarrollar aplicaciones más complejas que simplemente un «Hola Mundo».
Para comprender esto mejor, hagamos una analogía con algo cotidiano: el uso de dispositivos complejos como nuestras máquinas de café.
Exteriormente, una máquina de café parece simple: un botón, una pantalla, algunos orificios, y voilà, ¡café delicioso!
Pero su complejidad radica en su interior (imagen del manual de reparación).
Todos esos detalles internos, como tubos y calentadores, están meticulosamente diseñados para trabajar juntos. Sin embargo, están ocultos y protegidos para que los usuarios no interfieran accidentalmente.
En la programación orientada a objetos, aplicamos el mismo principio:
- Interfaz interna: Métodos y propiedades utilizados dentro de la clase, pero no accesibles externamente.
- Interfaz externa: Métodos y propiedades accesibles desde fuera de la clase.
En JavaScript, tenemos tres tipos de campos de objeto:
- Públicos: Accesibles desde cualquier lugar, forman parte de la interfaz externa.
- Protegidos: No son nativos en JavaScript, pero emulados. Accesibles dentro de la clase y sus subclases.
- Privados: Recientemente introducidos, comienzan con # y son accesibles solo dentro de la propia clase.
Para ilustrar esto, crearemos una clase CoffeeMachine en JavaScript con ejemplos de cada tipo de propiedad.
Proteger “amountWater”
Comencemos con una máquina de café simple:
class CoffeeMachine {
_amountWater = 0; // Cantidad de agua dentro de la máquina
constructor(power) {
this._power = power;
alert(`Se ha creado una máquina de café con una potencia de ${power}`);
}
// Métodos públicos
set amountWater(value) {
if (value < 0) {
value = 0;
}
this._amountWater = value;
}
get amountWater() {
return this._amountWater;
}
}
// Crear una instancia de la máquina de café
let coffeeMachine = new CoffeeMachine(100);
// Agregar agua
coffeeMachine.amountWater = 200;
Aquí, _amountWater
es una propiedad protegida que controlamos con los métodos set
y get
. Esto nos permite establecer reglas de negocio (como no permitir valores negativos) mientras mantenemos un control estricto sobre su acceso.
“Power” de solo lectura
La propiedad power
se establece solo una vez, al crear la máquina de café, y no cambia después:
class CoffeeMachine {
// ...
constructor(power) {
this._power = power;
}
// Solo getter para power (solo lectura)
get power() {
return this._power;
}
}
// Crear una instancia de la máquina de café
let coffeeMachine = new CoffeeMachine(100);
alert(`La potencia es: ${coffeeMachine.power}W`); // Potencia es: 100W
coffeeMachine.power = 25; // Error: no hay setter definido
Esta es una práctica común para propiedades que deben mantener su valor original y no deben modificarse después de la inicialización.
Funciones getter/setter
En lugar de usar la sintaxis get
y set
, podemos optar por métodos explícitos para mayor flexibilidad:
class CoffeeMachine {
_amountWater = 0;
setAmountWater(value) {
if (value < 0) value = 0;
this._amountWater = value;
}
getAmountWater() {
return this._amountWater;
}
}
let machine = new CoffeeMachine();
machine.setAmountWater(100);
Esta forma es útil cuando necesitamos manejar lógica adicional o múltiples argumentos en nuestras operaciones de set/get.
Campos privados
Recientemente, JavaScript introdujo campos privados que comienzan con #
. Estos son accesibles solo dentro de la clase:
class CoffeeMachine {
#waterLimit = 200;
#adjustWaterAmount(value) {
if (value < 0) return 0;
if (value > this.#waterLimit) return this.#waterLimit;
return value;
}
setAmountWater(value) {
this.#waterLimit = this.#adjustWaterAmount(value);
}
}
let coffeeMachine = new CoffeeMachine();
// Intentar acceder a campos privados genera un error
coffeeMachine.#adjustWaterAmount(123); // Error
coffeeMachine.#waterLimit = 1000; // Error
Los campos privados son particularmente útiles cuando necesitamos encapsular lógica compleja o datos sensibles que no deben ser manipulados desde el exterior de la clase.
Resumen
En conclusión, la delimitación clara de la interfaz interna y externa, conocida como encapsulamiento, es crucial en la programación orientada a objetos.
Ofrece protección contra modificaciones accidentales y oculta la complejidad interna, lo que facilita el mantenimiento y la escalabilidad del código.
Al utilizar propiedades y métodos públicos, protegidos y privados en JavaScript, podemos estructurar nuestras aplicaciones de manera que promuevan la seguridad, el mantenimiento y la reutilización del código.