Information Hiding
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 34 von 110.
Information Hiding ist das Prinzip hinter der Kapselung – die Idee, dass eine Klasse ihre internen Abläufe verbergen und nur das Notwendige offenlegen sollte. Sie haben die Mechanismen kennengelernt (private fields, getters, setters), aber das eigentliche Ziel ist der Entwurf von Klassen, die einfach zu verwenden und schwer falsch zu bedienen sind.
Betrachten Sie eine Klasse, die das Passwort eines Benutzers verwaltet. Die interne Speicherung und die Hashing-Logik sollten vollständig verborgen sein:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password'; // Vereinfachtes Beispiel
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}Externer Code sieht niemals den Passwort-Hash oder weiß, wie er gespeichert wird. Sie rufen einfach verifyPassword() auf. Diese Trennung bedeutet, dass Sie den Hashing-Algorithmus vollständig ändern könnten, ohne den Code zu beeinflussen, der diese Klasse verwendet.
Die entscheidende Erkenntnis besteht darin, Ihre Klasse von außen nach innen zu betrachten.
Fragen Sie sich: „Was benötigt jemand, der diese Klasse verwendet, tatsächlich?“ Geben Sie nur das frei und halten Sie alles andere privat. Dies schafft eine klare Grenze zwischen dem, was Ihre Klasse tut (ihre öffentliche Schnittstelle), und wie sie es tut (ihre private Implementierung).
Aufgabe
EinfachLassen Sie uns ein sicheres Wallet-System bauen, das Information Hiding in Aktion demonstriert. Sie werden ein digitales Wallet erstellen, das das Guthaben und den Transaktionsverlauf eines Benutzers verwaltet und dabei alle sensiblen internen Details vor externem Code vollständig verborgen hält.
Sie werden Ihren Code in zwei Dateien organisieren:
wallet.dart: Erstellen Sie eineWallet-Klasse, die Geld sicher verwaltet. Das Wallet sollte den Namen des Besitzers öffentlich speichern, aber alles andere sollte hinter einer sauberen öffentlichen Schnittstelle verborgen sein:- Das interne Guthaben und das Transaktionsprotokoll sollten privat sein – externer Code sollte niemals sehen, wie Sie Geld speichern oder verfolgen.
- Eine private Methode sollte das interne Aufzeichnen von Transaktionen übernehmen (Speichern von Einträgen wie
"Deposit: $50.0"oder"Withdrawal: $30.0"). - Stellen Sie eine öffentliche Methode
deposit(double amount)bereit, die Geld hinzufügt (nur wenn der Betrag positiv ist) und die Transaktion intern aufzeichnet. - Stellen Sie eine öffentliche Methode
withdraw(double amount)bereit, die Geld nur dann abhebt, wenn ausreichend Guthaben vorhanden ist und der Betrag positiv ist, und die Transaktion intern aufzeichnet. Geben Sietruezurück, wenn dies erfolgreich war, andernfallsfalse. - Stellen Sie einen öffentlichen Getter
balancebereit, der das aktuelle Guthaben zurückgibt (Nur-Lese-Zugriff). - Stellen Sie eine öffentliche Methode
getTransactionCount()bereit, die zurückgibt, wie viele Transaktionen aufgezeichnet wurden (ohne die tatsächliche Transaktionsliste offenzulegen). - Stellen Sie eine öffentliche Methode
getLastTransaction()bereit, die den neuesten Transaktions-String zurückgibt, oder"No transactions", falls keine vorhanden sind. - Fügen Sie eine Methode
printSummary()hinzu, die den Status des Wallets anzeigt.
main.dart: Importieren Sie Ihr Wallet und demonstrieren Sie, wie Information Hiding eine saubere, sichere Schnittstelle schafft:- Erstellen Sie ein Wallet für den Besitzer
"Sarah". - Zahlen Sie
100.0ein. - Zahlen Sie
50.0ein. - Versuchen Sie,
200.0abzuheben, und geben Sie"Withdrawal of 200.0: [success/failed]"basierend auf dem Ergebnis aus. - Heben Sie
30.0ab und geben Sie"Withdrawal of 30.0: [success/failed]"aus. - Rufen Sie
printSummary()auf. - Geben Sie
"Transaction count: [count]"aus. - Geben Sie
"Last transaction: [transaction]"aus.
- Erstellen Sie ein Wallet für den Besitzer
Die Methode printSummary() sollte Folgendes ausgeben:
=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]Beachten Sie, wie externer Code über klare, zweckgerichtete Methoden mit dem Wallet interagieren kann, ohne jemals zu wissen, wie das Guthaben gespeichert wird oder wie Transaktionen intern verfolgt werden. Die Implementierungsdetails sind vollständig verborgen – Sie könnten die Art und Weise, wie Transaktionen gespeichert werden, ändern, ohne den Code zu beeinflussen, der das Wallet verwendet.
Erwartete Ausgabe:
Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0Spickzettel
Information Hiding ist das Prinzip hinter der Kapselung – das Verbergen der internen Funktionsweise einer Klasse und das Offenlegen nur dessen, was notwendig ist.
Entwerfen Sie Klassen von außen nach innen: Legen Sie nur das offen, was Benutzer benötigen, und halten Sie alles andere privat. Dies schafft eine klare Grenze zwischen der öffentlichen Schnittstelle (was die Klasse tut) und der privaten Implementierung (wie sie es tut).
Beispiel für Information Hiding mit einem Passwort-Manager:
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;
}Externer Code sieht niemals den Passwort-Hash oder den Speichermechanismus – er verwendet nur verifyPassword(). Dies ermöglicht es, die interne Implementierung zu ändern, ohne den externen Code zu beeinflussen.
Probier es selbst
import 'wallet.dart';
void main() {
// TODO: Erstelle eine Wallet für die Besitzerin "Sarah"
// TODO: Zahle 100.0 ein
// TODO: Zahle 50.0 ein
// TODO: Versuche 200.0 abzuheben und gib das Ergebnis aus
// Format: "Withdrawal of 200.0: [success/failed]"
// TODO: Hebe 30.0 ab und gib das Ergebnis aus
// Format: "Withdrawal of 30.0: [success/failed]"
// TODO: Rufe printSummary() auf
// TODO: Gib die Anzahl der Transaktionen aus
// Format: "Transaction count: [count]"
// TODO: Gib die letzte Transaktion aus
// Format: "Last transaction: [transaction]"
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor