Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 eine Wallet-Klasse im Namespace Finance, 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, und true bei Erfolg, andernfalls false zurückgibt
    • Eine öffentliche Methode Withdraw(decimal amount), die nur dann vom Kontostand subtrahiert, wenn der Betrag positiv ist und den aktuellen Kontostand nicht überschreitet, und true bei Erfolg, andernfalls false zurückgibt
    • Eine öffentliche Methode CanAfford(decimal amount), die true zurückgibt, wenn der Kontostand größer oder gleich dem Betrag ist, andernfalls false
    • Eine öffentliche Methode GetStatement(), die intern die private Methode FormatBalance() verwendet und "Current balance: {formatted balance}" zurückgibt
    Beachten Sie, dass es keine Möglichkeit gibt, den Kontostand direkt zu lesen oder zu setzen – Sie können nur über kontrollierte Methoden mit ihm interagieren!
  • Program.cs: Erstellen Sie in Ihrer Hauptdatei ein Wallet und 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: False

Die 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
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung