Menu
Coddy logo textTech

Ocultamiento de información

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 34 de 110.

El ocultamiento de información es el principio detrás de la encapsulación: la idea de que una clase debe ocultar su funcionamiento interno y solo exponer lo que sea necesario. Has aprendido la mecánica (campos privados, getters, setters), pero el verdadero objetivo es diseñar clases que sean fáciles de usar y difíciles de usar de forma incorrecta.

Considera una clase que gestiona la contraseña de un usuario. El almacenamiento interno y la lógica de hashing deben estar completamente ocultos:

class UserAccount {
  String _username;
  String _passwordHash;
  
  UserAccount(this._username, String password) 
      : _passwordHash = _hashPassword(password);
  
  static String _hashPassword(String password) {
    return 'hashed_$password';  // Ejemplo simplificado
  }
  
  bool verifyPassword(String password) {
    return _passwordHash == _hashPassword(password);
  }
  
  String get username => _username;
}

El código externo nunca ve el hash de la contraseña ni sabe cómo se almacena. Simplemente llaman a verifyPassword(). Esta separación significa que podrías cambiar completamente el algoritmo de hashing sin afectar a ningún código que utilice esta clase.

La idea clave es pensar en tu clase de afuera hacia adentro.

Pregúntate: "¿Qué es lo que realmente necesita alguien que usa esta clase?" Expón solo eso y mantén todo lo demás privado. Esto crea un límite claro entre lo que hace tu clase (su interfaz pública) y cómo lo hace (su implementación privada).

challenge icon

Desafío

Fácil

Vamos a construir un sistema de billetera segura que demuestre la ocultación de información en acción. Crearás una billetera digital que gestione el saldo y el historial de transacciones de un usuario, manteniendo todos los detalles internos sensibles completamente ocultos del código externo.

Organizarás tu código en dos archivos:

  • wallet.dart: Crea una clase Wallet que gestione el dinero de forma segura. La billetera debe almacenar el nombre del propietario de forma pública, pero todo lo demás debe estar oculto tras una interfaz pública limpia:
    • El saldo interno y el registro de transacciones deben ser privados; el código externo nunca debe ver cómo almacenas o rastreas el dinero.
    • Un método privado debe encargarse de registrar las transacciones internamente (almacenando entradas como "Deposit: $50.0" o "Withdrawal: $30.0").
    • Proporciona un método público deposit(double amount) que añada dinero (solo si la cantidad es positiva) y registre la transacción internamente.
    • Proporciona un método público withdraw(double amount) que retire dinero solo si hay fondos suficientes y la cantidad es positiva, registrando la transacción internamente. Devuelve true si tiene éxito, false en caso contrario.
    • Proporciona un getter público balance que devuelva el saldo actual (acceso de solo lectura).
    • Proporciona un método público getTransactionCount() que devuelva cuántas transacciones se han registrado (sin exponer la lista de transacciones real).
    • Proporciona un método público getLastTransaction() que devuelva la cadena de la transacción más reciente, o "No transactions" si no existe ninguna.
    • Incluye un método printSummary() que muestre el estado de la billetera.
  • main.dart: Importa tu billetera y demuestra cómo la ocultación de información crea una interfaz limpia y segura:
    • Crea una billetera para el propietario "Sarah".
    • Deposita 100.0.
    • Deposita 50.0.
    • Intenta retirar 200.0 e imprime "Withdrawal of 200.0: [success/failed]" basándote en el resultado.
    • Retira 30.0 e imprime "Withdrawal of 30.0: [success/failed]".
    • Llama a printSummary().
    • Imprime "Transaction count: [count]".
    • Imprime "Last transaction: [transaction]".

El método printSummary() debe imprimir:

=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]

Observa cómo el código externo puede interactuar con la billetera a través de métodos claros y con un propósito definido, sin saber nunca cómo se almacena el saldo o cómo se rastrean las transacciones internamente. Los detalles de implementación están completamente ocultos: podrías cambiar la forma en que se almacenan las transacciones sin afectar a ningún código que utilice la billetera.

Salida esperada:

Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0

Hoja de referencia

El ocultamiento de información es el principio detrás del encapsulamiento: ocultar el funcionamiento interno de una clase y exponer solo lo necesario.

Diseña las clases de afuera hacia adentro: expón solo lo que los usuarios necesitan, mantén todo lo demás privado. Esto crea un límite claro entre la interfaz pública (lo que hace la clase) y la implementación privada (cómo lo hace).

Ejemplo de ocultamiento de información con un gestor de contraseñas:

class UserAccount {
  String _username;
  String _passwordHash;
  
  UserAccount(this._username, String password) 
      : _passwordHash = _hashPassword(password);
  
  static String _hashPassword(String password) {
    return 'hashed_$password';
  }
  
  bool verifyPassword(String password) {
    return _passwordHash == _hashPassword(password);
  }
  
  String get username => _username;
}

El código externo nunca ve el hash de la contraseña ni el mecanismo de almacenamiento; solo utilizan verifyPassword(). Esto permite cambiar la implementación interna sin afectar al código externo.

Pruébalo tú mismo

import 'wallet.dart';

void main() {
  // TODO: Crear una billetera para el propietario "Sarah"
  
  // TODO: Depositar 100.0
  
  // TODO: Depositar 50.0
  
  // TODO: Intentar retirar 200.0 e imprimir el resultado
  // Formato: "Withdrawal of 200.0: [success/failed]"
  
  // TODO: Retirar 30.0 e imprimir el resultado
  // Formato: "Withdrawal of 30.0: [success/failed]"
  
  // TODO: Llamar a printSummary()
  
  // TODO: Imprimir el recuento de transacciones
  // Formato: "Transaction count: [count]"
  
  // TODO: Imprimir la última transacción
  // Formato: "Last transaction: [transaction]"
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos