Clases, objetos y estado
Un objeto combina identidad, estado y comportamiento bajo invariantes. Una clase puede construir ese contrato, pero no todo dato necesita convertirse en objeto mutable.
Para este tema conviene tener claro:Funciones, parámetros y alcance
Una clase no mejora un programa por envolver variables con métodos. Su valor aparece cuando protege reglas sobre un estado que cambia y ofrece operaciones con significado para el dominio.
Identidad, estado y comportamiento
Un objeto tiene identidad: dos cuentas con el mismo saldo pueden seguir siendo cuentas distintas. Su estado reúne datos observables y su comportamiento define transiciones válidas.
Dos cuentas creadas por separado, con el mismo saldo.
Una clase describe cómo construir instancias y qué operaciones comparten. El objeto concreto mantiene sus propios valores; la clase no es el objeto, del mismo modo que un plano no es la casa.
Antes de seguir, predecí
Lo que la clase promete sostener
El constructor debería establecer un estado válido y cada método público conservarlo:
class Counter {
private value = 0;
increment(): void {
this.value += 1;
}
current(): number {
return this.value;
}
}Si el contador no admite negativos, no exponer una asignación arbitraria evita estados imposibles.
Métodos que son acciones, no getters
Los métodos deberían expresar acciones, no sólo replicar getters y setters para cada campo. Una
cuenta withdraw(amount) puede validar monto, saldo y registrar la operación en una sola transición.
Cuando quien llama extrae datos, decide reglas y vuelve a asignarlos, la lógica queda fuera del objeto y puede duplicarse. Pedirle al objeto que realice la operación mantiene la invariante cerca del estado que protege.
Escena 1 — Dos nombres, un objeto
paso a paso
Cargando la escena…
Cuándo no hace falta identidad
No todo necesita identidad mutable. Una coordenada o un monto pueden modelarse como valores inmutables: dos instancias con el mismo contenido representan lo mismo y una operación devuelve otro valor.
La mutabilidad es útil para entidades que evolucionan, pero agrega historia y aliasing. Antes de crear una clase con setters, preguntá si un registro inmutable y funciones puras expresarían mejor el problema.
El constructor que no deja construir algo inválido
class Account {
private balance: number;
private constructor(balance: number, readonly id: string) {
this.balance = balance;
}
// la única puerta de entrada: valida y recién después construye
static open(id: string, initial: number): Account {
if (initial < 0) throw new Error(`saldo inicial inválido: ${initial}`);
return new Account(initial, id);
}
withdraw(amount: number): void {
if (amount <= 0) throw new Error('el monto tiene que ser positivo');
if (amount > this.balance) throw new Error('saldo insuficiente');
this.balance -= amount; // la invariante «balance >= 0» se conserva
}
get available(): number {
return this.balance;
}
}Lo que hace útil a esa clase es una sola idea: no existe forma de tener un Account con saldo
negativo. El constructor es privado, la puerta de entrada valida, y cada método público
comprueba antes de modificar. Esa propiedad —cierta para cualquier secuencia de llamadas— es la
invariante de la clase.
Comparalo con la alternativa de un objeto plano y funciones sueltas: ahí cualquiera puede escribir
cuenta.balance = -500 y el resto del sistema empieza a razonar sobre algo imposible.
Cuándo una clase y cuándo no
| Situación | Qué conviene | Por qué |
|---|---|---|
| Hay invariantes que sostener | una clase | los métodos son la única puerta |
| Sólo son datos que viajan | un tipo o un registro | una clase sin comportamiento es ceremonia |
| La lógica no depende de estado | funciones | una clase con un solo método es una función disfrazada |
| Hace falta reemplazar la implementación | una interfaz y una clase | permite inyectar otra en los tests |
| El estado es compartido entre hilos | una clase, con cuidado explícito | hay que decidir la sincronización en un solo lugar |
Cierre
Un buen objeto concentra estado, transiciones e invariantes. Una clase es una herramienta para construir ese límite, no el objetivo del diseño. Modelá identidad cuando existe y preferí valores simples cuando no hace falta conservar historia.
Autoevaluación
¿Lo entendiste?
Práctica