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).
Desafío
FácilVamos 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 claseWalletque 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. Devuelvetruesi tiene éxito,falseen caso contrario. - Proporciona un getter público
balanceque 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.0e imprime"Withdrawal of 200.0: [success/failed]"basándote en el resultado. - Retira
30.0e imprime"Withdrawal of 30.0: [success/failed]". - Llama a
printSummary(). - Imprime
"Transaction count: [count]". - Imprime
"Last transaction: [transaction]".
- Crea una billetera para el propietario
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.0Hoja 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]"
}
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
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository