Atlasingeniería

Algoritmos y programaciónProgramación orientada a objetosTema 1

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.

1 / 6
Cuatro comparaciones que dan lo que uno no espera hasta que ve la columna de la identidad. Dos objetos iguales no son el mismo objeto, y un objeto con dos nombres es uno solo: de ahí salen la mitad de los errores raros con estructuras compartidas.

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í

Creás dos cuentas por separado, las dos con saldo 1000, y comparás con ===. ¿Qué da?

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…

Asignar una variable a otra no copia el objeto. Y el final es el que más cuesta aceptar: dos cuentas con exactamente el mismo saldo no son la misma cuenta, porque la identidad no la dan los datos.

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ónQué convienePor qué
Hay invariantes que sosteneruna claselos métodos son la única puerta
Sólo son datos que viajanun tipo o un registrouna clase sin comportamiento es ceremonia
La lógica no depende de estadofuncionesuna clase con un solo método es una función disfrazada
Hace falta reemplazar la implementaciónuna interfaz y una clasepermite inyectar otra en los tests
El estado es compartido entre hilosuna clase, con cuidado explícitohay que decidir la sincronización en un solo lugar
La tercera fila es la más frecuente en código real: un montón de clases con un solo método público son funciones a las que alguien les puso un envoltorio.

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?

¿Cuándo aporta algo una clase?
Quien llama extrae el saldo, decide si alcanza y vuelve a asignarlo. ¿Cuál es el problema?
Dos cuentas con el mismo saldo, ¿son la misma cuenta?
El contador no admite negativos. ¿Qué es lo que hay que evitar exponer?