Implementación de ocultamiento de datos
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 33 de 70.
La ocultación de datos va más allá de simplemente usar campos private. Se trata de diseñar su clase de modo que los detalles de implementación interna sean completamente invisibles para el código externo, exponiendo solo lo necesario a través de una interfaz pública limpia.
Considera una clase que rastrea la contraseña de un usuario. No quieres que nadie acceda a la contraseña sin procesar, ni siquiera para leerla:
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}Observe que no hay forma de recuperar la contraseña o incluso el hash. El código externo solo puede establecer una contraseña o verificar una; el mecanismo de almacenamiento interno está completamente oculto. El método HashPassword también es privado porque es un detalle de implementación que podría cambiar sin afectar la forma en que se utiliza la clase.
Este enfoque protege su clase contra el mal uso y le otorga la libertad de cambiar los aspectos internos más adelante. Si decide utilizar un algoritmo de hashing diferente, ningún código externo se verá afectado porque nada dependía de cómo se almacenaba la contraseña.
Desafío
FácilVamos a construir un sistema de billetera segura que demuestre la verdadera ocultación de datos. Crearás una billetera digital donde el saldo esté completamente protegido del acceso directo: el código externo solo puede depositar, retirar y verificar si las transacciones son posibles, pero nunca ver o manipular el saldo real directamente.
Organizarás tu código en dos archivos:
Wallet.cs: Crea una claseWalleten el espacio de nombresFinanceque mantenga su saldo completamente oculto. La billetera debe tener:- Un campo privado para almacenar el saldo (decimal)
- Un método privado
FormatBalance()que devuelva el saldo formateado como una cadena de moneda como"$123.45"; este es un ayudante interno que no debe ser expuesto - Un método público
Deposit(decimal amount)que sume al saldo solo si el monto es positivo, devolviendotruesi tiene éxito,falseen caso contrario - Un método público
Withdraw(decimal amount)que reste del saldo solo si el monto es positivo y no excede el saldo actual, devolviendotruesi tiene éxito,falseen caso contrario - Un método público
CanAfford(decimal amount)que devuelvatruesi el saldo es mayor o igual al monto,falseen caso contrario - Un método público
GetStatement()que utilice internamente el método privadoFormatBalance()y devuelva"Current balance: {formatted balance}"
Program.cs: En tu archivo principal, crea unaWallety realiza una serie de transacciones utilizando valores de entrada. Muestra cómo la billetera protege su estado interno mientras sigue proporcionando funcionalidad útil a través de su interfaz pública.
Recibirás tres entradas:
- Monto del depósito inicial (decimal)
- Monto del retiro a intentar (decimal)
- Monto para verificar la asequibilidad (decimal)
Imprime la salida en este formato:
Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}Por ejemplo, si las entradas son 100.50, 30.25 y 80.00, la salida debería ser:
Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: FalseLa idea clave aquí es que el código externo nunca toca directamente el campo del saldo. Incluso la cadena del saldo formateada proviene de un método público que utiliza internamente un ayudante privado. ¡Esto es ocultación de datos: los detalles de implementación son completamente invisibles y eres libre de cambiar cómo se almacena o formatea el saldo sin romper ningún código que use tu billetera!
Hoja de referencia
El ocultamiento de datos significa diseñar clases de modo que los detalles de implementación interna sean completamente invisibles para el código externo, exponiendo solo lo necesario a través de una interfaz pública limpia.
Use campos private para almacenar datos sensibles y proporcione acceso controlado a través de métodos public:
public class UserAccount
{
private string passwordHash;
public void SetPassword(string password)
{
passwordHash = HashPassword(password);
}
public bool VerifyPassword(string password)
{
return HashPassword(password) == passwordHash;
}
private string HashPassword(string password)
{
return password.GetHashCode().ToString();
}
}Principios clave:
- Sin acceso directo a los datos internos (sin getters para campos sensibles)
- Métodos auxiliares privados para los detalles de implementación
- Los métodos públicos solo proporcionan operaciones controladas
- El mecanismo de almacenamiento interno está completamente oculto
Esto protege a la clase del mal uso y permite cambiar la implementación interna sin romper el código externo.
Pruébalo tú mismo
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Leer los valores de entrada
decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
decimal checkAmount = Convert.ToDecimal(Console.ReadLine());
// TODO: Crear una instancia de Wallet
// TODO: Realizar el depósito e imprimir el resultado en el formato: "Deposit {amount}: {True/False}"
// TODO: Imprimir el estado de cuenta usando GetStatement()
// TODO: Realizar el retiro e imprimir el resultado en el formato: "Withdraw {amount}: {True/False}"
// TODO: Imprimir el estado de cuenta usando GetStatement()
// TODO: Verificar la asequibilidad e imprimir el resultado en el formato: "Can afford {amount}: {True/False}"
}
}
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 OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes