Implementierung von Data Hiding
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 33 von 70.
Datenkapselung geht über die bloße Verwendung von private Feldern hinaus. Es geht darum, Ihre Klasse so zu entwerfen, dass interne Implementierungsdetails für externen Code vollständig unsichtbar sind und nur das Notwendige über eine saubere öffentliche Schnittstelle offengelegt wird.
Betrachten Sie eine Klasse, die das Passwort eines Benutzers verwaltet. Sie möchten nicht, dass jemand auf das Passwort im Klartext zugreift – nicht einmal, um es zu lesen:
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();
}
}Beachten Sie, dass es keine Möglichkeit gibt, das Passwort oder gar den Hash abzurufen. Externer Code kann nur ein Passwort festlegen oder eines verifizieren – der interne Speichermechanismus ist vollständig verborgen. Die Methode HashPassword ist ebenfalls privat, da es sich um ein Implementierungsdetail handelt, das sich ändern könnte, ohne die Verwendung der Klasse zu beeinflussen.
Dieser Ansatz schützt Ihre Klasse vor Missbrauch und gibt Ihnen die Freiheit, Interna später zu ändern. Wenn Sie sich entscheiden, einen anderen Hashing-Algorithmus zu verwenden, bricht kein externer Code ab, da nichts davon abhing, wie das Passwort gespeichert wurde.
Aufgabe
EinfachLassen Sie uns ein sicheres Wallet-System bauen, das echtes Data Hiding (Datenkapselung) demonstriert. Sie werden ein digitales Wallet erstellen, bei dem der Kontostand vollständig vor direktem Zugriff geschützt ist – externer Code kann nur einzahlen, abheben und prüfen, ob Transaktionen möglich sind, aber niemals den tatsächlichen Kontostand direkt sehen oder manipulieren.
Sie werden Ihren Code auf zwei Dateien aufteilen:
Wallet.cs: Erstellen Sie eineWallet-Klasse im NamespaceFinance, die ihren Kontostand vollständig verborgen hält. Das Wallet sollte Folgendes haben:- Ein privates Feld zum Speichern des Kontostands (decimal)
- Eine private Methode
FormatBalance(), die den Kontostand als Währungsstring wie"$123.45"formatiert zurückgibt – dies ist ein interner Helfer, der nicht nach außen hin exponiert werden sollte - Eine öffentliche Methode
Deposit(decimal amount), die nur dann zum Kontostand addiert, wenn der Betrag positiv ist, undtruebei Erfolg, andernfallsfalsezurückgibt - Eine öffentliche Methode
Withdraw(decimal amount), die nur dann vom Kontostand subtrahiert, wenn der Betrag positiv ist und den aktuellen Kontostand nicht überschreitet, undtruebei Erfolg, andernfallsfalsezurückgibt - Eine öffentliche Methode
CanAfford(decimal amount), dietruezurückgibt, wenn der Kontostand größer oder gleich dem Betrag ist, andernfallsfalse - Eine öffentliche Methode
GetStatement(), die intern die private MethodeFormatBalance()verwendet und"Current balance: {formatted balance}"zurückgibt
Program.cs: Erstellen Sie in Ihrer Hauptdatei einWalletund führen Sie eine Reihe von Transaktionen mit Eingabewerten durch. Zeigen Sie, wie das Wallet seinen internen Zustand schützt und dennoch nützliche Funktionalität über seine öffentliche Schnittstelle bereitstellt.
Sie erhalten drei Eingaben:
- Anfänglicher Einzahlungsbetrag (decimal)
- Betrag für den Abhebungsversuch (decimal)
- Betrag, für den die Erschwinglichkeit geprüft werden soll (decimal)
Geben Sie die Ausgabe in diesem Format aus:
Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}Wenn die Eingaben beispielsweise 100.50, 30.25 und 80.00 sind, sollte die Ausgabe wie folgt aussehen:
Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: FalseDie entscheidende Erkenntnis hierbei ist, dass externer Code niemals direkt das Feld für den Kontostand berührt. Sogar der formatierte Kontostand-String kommt über eine öffentliche Methode, die intern einen privaten Helfer verwendet. Das ist Data Hiding – die Implementierungsdetails sind völlig unsichtbar, und Sie können die Art und Weise, wie der Kontostand gespeichert oder formatiert wird, ändern, ohne Code zu zerstören, der Ihr Wallet verwendet!
Spickzettel
Datenkapselung bedeutet, Klassen so zu entwerfen, dass interne Implementierungsdetails für externen Code vollständig unsichtbar sind und nur das Notwendige über eine saubere öffentliche Schnittstelle offengelegt wird.
Verwenden Sie private Felder, um sensible Daten zu speichern, und ermöglichen Sie den kontrollierten Zugriff über public Methoden:
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();
}
}Grundprinzipien:
- Kein direkter Zugriff auf interne Daten (keine Getter für sensible Felder)
- Private Hilfsmethoden für Implementierungsdetails
- Öffentliche Methoden bieten nur kontrollierte Operationen an
- Der interne Speichermechanismus ist vollständig verborgen
Dies schützt die Klasse vor Missbrauch und ermöglicht es, die interne Implementierung zu ändern, ohne externen Code zu beeinträchtigen.
Probier es selbst
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Eingabewerte lesen
decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
decimal checkAmount = Convert.ToDecimal(Console.ReadLine());
// TODO: Eine Wallet-Instanz erstellen
// TODO: Einzahlung durchführen und Ergebnis im Format "Deposit {amount}: {True/False}" ausgeben
// TODO: Den Kontoauszug mit GetStatement() ausgeben
// TODO: Auszahlung durchführen und Ergebnis im Format "Withdraw {amount}: {True/False}" ausgeben
// TODO: Den Kontoauszug mit GetStatement() ausgeben
// TODO: Erschwinglichkeit prüfen und Ergebnis im Format "Can afford {amount}: {True/False}" ausgeben
}
}
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 DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten