Nascondimento delle informazioni
Fa parte della sezione Programmazione orientata agli oggetti del percorso Dart di Coddy. Lezione 34 di 110.
L'occultamento delle informazioni è il principio alla base dell'incapsulamento: l'idea che una classe dovrebbe nascondere il proprio funzionamento interno ed esporre solo ciò che è necessario. Hai imparato i meccanismi (campi privati, getter, setter), ma il vero obiettivo è progettare classi facili da usare e difficili da usare in modo scorretto.
Considera una classe che gestisce la password di un utente. La logica interna di archiviazione e hashing dovrebbe essere completamente nascosta:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password'; // Simplified example
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}Il codice esterno non vede mai l'hash della password né sa come viene memorizzato. Si limita a chiamare verifyPassword(). Questa separazione significa che potresti cambiare completamente l'algoritmo di hashing senza influire su alcun codice che usa questa classe.
L’intuizione chiave è pensare alla tua classe dall’esterno verso l’interno.
Chiediti: "Di cosa ha davvero bisogno chi utilizza questa classe?" Esponi solo questo e mantieni tutto il resto privato. In questo modo si crea un confine chiaro tra ciò che fa la tua classe (la sua interfaccia pubblica) e il modo in cui lo fa (la sua implementazione privata).
Sfida
FacileCreiamo un sistema di portafoglio sicuro che dimostri in pratica l'occultamento delle informazioni. Creerai un portafoglio digitale che gestisce il saldo e la cronologia delle transazioni di un utente, mantenendo tutti i dettagli interni sensibili completamente nascosti al codice esterno.
Organizzerai il codice in due file:
wallet.dart: crea una classeWalletche gestisca il denaro in modo sicuro. Il portafoglio dovrebbe memorizzare pubblicamente il nome del proprietario, ma tutto il resto dovrebbe essere nascosto dietro un'interfaccia pubblica chiara:- Il saldo interno e il registro delle transazioni dovrebbero essere privati: il codice esterno non dovrebbe mai vedere come memorizzi o tieni traccia del denaro
- Un metodo privato dovrebbe gestire internamente la registrazione delle transazioni (memorizzando voci come
"Deposit: $50.0"o"Withdrawal: $30.0") - Fornisci un metodo pubblico
deposit(double amount)che aggiunga denaro (solo se l'importo è positivo) e registri internamente la transazione - Fornisci un metodo pubblico
withdraw(double amount)che prelevi denaro solo se i fondi sono sufficienti e l'importo è positivo, registrando internamente la transazione. Restituiscitruese l'operazione riesce, altrimentifalse - Fornisci un getter pubblico
balanceche restituisca il saldo corrente (accesso in sola lettura) - Fornisci un metodo pubblico
getTransactionCount()che restituisca il numero di transazioni registrate (senza esporre l'elenco effettivo delle transazioni) - Fornisci un metodo pubblico
getLastTransaction()che restituisca la stringa della transazione più recente, oppure"No transactions"se non ce ne sono - Includi un metodo
printSummary()che mostri lo stato del portafoglio
main.dart: importa il tuo portafoglio e mostra come l'occultamento delle informazioni crei un'interfaccia chiara e sicura:- Crea un portafoglio per il proprietario
"Sarah" - Deposita
100.0 - Deposita
50.0 - Prova a prelevare
200.0e stampa"Withdrawal of 200.0: [success/failed]"in base al risultato - Preleva
30.0e stampa"Withdrawal of 30.0: [success/failed]" - Chiama
printSummary() - Stampa
"Transaction count: [count]" - Stampa
"Last transaction: [transaction]"
- Crea un portafoglio per il proprietario
Il metodo printSummary() dovrebbe stampare:
=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]Nota come il codice esterno possa interagire con il portafoglio tramite metodi chiari e mirati senza sapere come viene memorizzato il saldo o come vengono tracciate internamente le transazioni. I dettagli di implementazione sono completamente nascosti: potresti modificare il modo in cui vengono memorizzate le transazioni senza influire sul codice che usa il portafoglio.
Output previsto:
Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0Provalo tu
import 'wallet.dart';
void main() {
// TODO: Crea un monedero para la propietaria "Sarah"
// TODO: Deposita 100.0
// TODO: Deposita 50.0
// TODO: Intenta retirar 200.0 e imprime el resultado
// Formato: "Withdrawal of 200.0: [success/failed]"
// TODO: Retira 30.0 e imprime el resultado
// Formato: "Withdrawal of 30.0: [success/failed]"
// TODO: Llama a printSummary()
// TODO: Imprime el número de transacciones
// Formato: "Transaction count: [count]"
// TODO: Imprime la última transacción
// Formato: "Last transaction: [transaction]"
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniLibrerie e importazioniIntroduzione alla programmazione orientata agli oggettiClassi e oggettiLa parola chiave thisMetodiVariabili di istanzaFondamenti dei costruttoriRiepilogo - Calcolatrice semplice4Sicurezza dei valori null
Introduzione alla sicurezza dei valori nullNullable e non nullableGli operatori ? e !La keyword late e la sicurezza dei valori nullOperatori null-awareSicurezza dei valori null nelle classiRiepilogo - Sistema di profili utente7Classi astratte e interfacce
Classi astratteMetodi astrattiInterfacce in DartInterfacce impliciteImplementare vs estendereInterfacce multipleRiepilogo - Calcolatore di forme10Collezioni e generici
Panoramica di List, Set e MapCollezioni sicure rispetto ai tipiClassi genericheMetodi genericiVincoli sui genericiIterable e IteratorRiepilogo - Archiviazione generica13Concetti avanzati di OOP
Composizione vs ereditarietàMetodi di estensioneClassi chiamabiliClassi sealed (Dart 3)Record (Dart 3)Pattern e matching (3.0)Enum con metodi16Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e Utente2I costruttori in Dart
Costruttore predefinitoCostruttori con nomeListe di inizializzazioneCostruttori costantiCostruttori factoryCostruttori di reindirizzamentoRiepilogo - Crea forme5Incapsulamento
Membri pubblici e privatiConvenzione del prefisso _Visibilità a livello di libreriaApprofondimento su getter e setterNascondimento delle informazioniRiepilogo - Registri degli studenti8Mixin
Introduzione ai MixinCreare MixinUsare più MixinParola chiave on nei MixinMixin vs ereditarietàMixin vs interfacciaRiepilogo - Sistema animale11Metodi speciali
Sovrascrittura di toString()Sovrascrittura di hashCode e ==Interfaccia ComparableMetodo call()Sovrascrittura di noSuchMethodRiepilogo - Collezione personalizzata14Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneSingletonFactoryObserverStrategy3Proprietà delle classi
Membri di istanza e staticiCampi final e constVariabili lateMetodi e campi staticiGetter e setterRiepilogo - Gestore di conti bancari6Ereditarietà
Ereditarietà di baseLa parola chiave superOverride dei metodiL’annotazione @overrideLa parola chiave final per le classiCostruttori ed ereditarietàRiepilogo - Gerarchia dei dipendenti9Polimorfismo
Fondamenti di polimorfismoPolimorfismo tramite interfacceControllo del tipo a runtimeGli operatori is e asParola chiave covariantRiepilogo - Elaboratore di pagamenti12OOP asincrona
Future e async/awaitNozioni di base sugli streamController degli streamCostruttori asincroniAsync nei metodi di classeRiepilogo - Recupero dei dati15Pattern di progettazione Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern RepositoryEsercitati da solo: Compilatore Dart online