Miembros públicos vs. privados
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 30 de 110.
El encapsulamiento es uno de los principios fundamentales de la programación orientada a objetos. Se trata de controlar el acceso a los datos internos de una clase, decidiendo qué debe ser visible para el mundo exterior y qué debe permanecer oculto.
En Dart, los miembros de clase (campos y métodos) son públicos por defecto. Esto significa que cualquier código puede acceder a ellos directamente:
class BankAccount {
String owner;
double balance;
BankAccount(this.owner, this.balance);
}
var account = BankAccount('Alice', 1000);
account.balance = -500; // ¡Cualquiera puede modificar esto directamente!Esto es problemático. Nada impide establecer un saldo no válido.
Para proteger los datos internos, Dart utiliza miembros privados. Un miembro se vuelve privado cuando su nombre comienza con un guion bajo _:
class BankAccount {
String owner;
double _balance; // Privado - comienza con guion bajo
BankAccount(this.owner, this._balance);
double get balance => _balance; // Acceso controlado
}Ahora no se puede acceder directamente a _balance desde fuera de la librería donde se define la clase. El código externo debe usar el getter público en su lugar. Esto te permite controlar cómo se leen y modifican los datos, asegurando que tus objetos siempre permanezcan en un estado válido.
Desafío
FácilVamos a construir un gestor de contraseñas sencillo que demuestre la diferencia entre los miembros públicos y privados. Verás cómo el prefijo de guion bajo protege los datos sensibles de ser accedidos directamente desde fuera de la librería de la clase.
Crearás dos archivos para organizar tu código:
credential.dart: Define una claseCredentialque almacene información de inicio de sesión para un sitio web. La clase debe tener:- Un
String websitepúblico: no hay problema en que cualquiera vea para qué sitio web es esto - Un
String usernamepúblico: el nombre de usuario puede ser visible - Un
String _passwordprivado: la contraseña real debe estar protegida - Un constructor que reciba los tres valores
- Un getter público
passwordque devuelva el_passwordprivado (acceso controlado) - Un método
getMaskedPassword()que devuelva la contraseña con todos los caracteres reemplazados por asteriscos*(de la misma longitud que la contraseña real) - Un método
displayCredential()que imprima la información de la credencial de forma segura (mostrando la contraseña enmascarada, no la real)
- Un
main.dart: Importa tu clase de credenciales y demuestra cómo la encapsulación protege la contraseña:- Crea una credencial para el sitio web
'github.com', usuario'dev_user'y contraseña'secret123' - Crea otra credencial para el sitio web
'email.com', usuario'john_doe'y contraseña'myP@ss' - Para cada credencial, llama a
displayCredential(), luego imprimeActual password length: [length]usando el getter público para acceder a la longitud de la contraseña
- Crea una credencial para el sitio web
El método displayCredential() debe imprimir en este formato:
--- Credential ---
Website: [website]
Username: [username]
Password: [maskedPassword]Salida esperada:
--- Credential ---
Website: github.com
Username: dev_user
Password: *********
Actual password length: 9
--- Credential ---
Website: email.com
Username: john_doe
Password: ******
Actual password length: 6Hoja de referencia
El encapsulamiento controla el acceso a los datos internos de una clase al decidir qué debe ser visible y qué debe permanecer oculto.
En Dart, los miembros de la clase son públicos por defecto:
class BankAccount {
String owner;
double balance; // Público - se puede acceder directamente
BankAccount(this.owner, this.balance);
}
var account = BankAccount('Alice', 1000);
account.balance = -500; // Se permite la modificación directaPara proteger los datos internos, use miembros privados anteponiendo un guion bajo _ al nombre:
class BankAccount {
String owner;
double _balance; // Privado - comienza con un guion bajo
BankAccount(this.owner, this._balance);
double get balance => _balance; // Acceso controlado a través de un getter
}No se puede acceder directamente a los miembros privados desde fuera de la librería donde se define la clase. Utilice getters y métodos públicos para proporcionar un acceso controlado a los datos privados, asegurando que los objetos permanezcan en un estado válido.
Pruébalo tú mismo
import 'credential.dart';
void main() {
// TODO: Crea una credencial para github.com
// website: 'github.com', username: 'dev_user', password: 'secret123'
// TODO: Llama a displayCredential() en la primera credencial
// TODO: Imprime la longitud real de la contraseña usando el getter público
// Formato: "Actual password length: [length]"
// TODO: Crea una credencial para email.com
// website: 'email.com', username: 'john_doe', password: 'myP@ss'
// TODO: Llama a displayCredential() en la segunda credencial
// TODO: Imprime la longitud real de la contraseña usando el getter público
}
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