Implementacja ukrywania danych
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 33 z 70.
Ukrywanie danych wykracza poza samo używanie pól private. Chodzi o zaprojektowanie klasy tak, aby szczegóły jej wewnętrznej implementacji były całkowicie niewidoczne dla kodu zewnętrznego, a przez przejrzysty interfejs publiczny udostępniała tylko to, co niezbędne.
Rozważ klasę, która śledzi hasło użytkownika. Nie chcesz, aby ktokolwiek miał dostęp do niezaszyfrowanego hasła — nawet po to, by je odczytać:
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();
}
}Zauważ, że nie ma sposobu na odzyskanie hasła ani nawet skrótu. Zewnętrzny kod może jedynie ustawić hasło lub je zweryfikować — wewnętrzny mechanizm przechowywania jest całkowicie ukryty. Metoda HashPassword jest również prywatna, ponieważ stanowi szczegół implementacji, który może ulec zmianie bez wpływu na sposób użycia klasy.
To podejście chroni Twoją klasę przed niewłaściwym użyciem i daje Ci swobodę późniejszej zmiany szczegółów implementacji. Jeśli zdecydujesz się użyć innego algorytmu haszującego, żaden zewnętrzny kod nie przestanie działać, ponieważ nic nie zależało od sposobu przechowywania hasła.
Wyzwanie
ŁatwyZbudujmy bezpieczny system portfela, który pokazuje, na czym polega prawdziwe ukrywanie danych. Utworzysz portfel cyfrowy, którego saldo jest całkowicie chronione przed bezpośrednim dostępem — zewnętrzny kod może jedynie wpłacać środki, wypłacać je i sprawdzać, czy transakcje są możliwe, ale nigdy nie może bezpośrednio zobaczyć ani zmieniać właściwego salda.
Rozmieścisz swój kod w dwóch plikach:
Wallet.cs: Utwórz klasęWalletw przestrzeni nazwFinance, która całkowicie ukrywa saldo. Portfel powinien mieć:- Pole prywatne do przechowywania salda (decimal)
- Prywatną metodę
FormatBalance(), która zwraca saldo sformatowane jako ciąg znaków reprezentujący walutę, na przykład"$123.45"— to wewnętrzna metoda pomocnicza, która nie powinna być udostępniana - Metodę publiczną
Deposit(decimal amount), która dodaje środki do salda tylko wtedy, gdy kwota jest dodatnia, i zwracatruew przypadku powodzenia, a w przeciwnym raziefalse - Metodę publiczną
Withdraw(decimal amount), która odejmuje środki od salda tylko wtedy, gdy kwota jest dodatnia i nie przekracza bieżącego salda, i zwracatruew przypadku powodzenia, a w przeciwnym raziefalse - Metodę publiczną
CanAfford(decimal amount), która zwracatrue, jeśli saldo jest większe lub równe podanej kwocie, a w przeciwnym raziefalse - Metodę publiczną
GetStatement(), która wewnętrznie używa prywatnej metodyFormatBalance()i zwraca"Current balance: {formatted balance}"
Program.cs: W pliku głównym utwórz obiektWalleti wykonaj serię transakcji, używając wartości wejściowych. Pokaż, jak portfel chroni swój stan wewnętrzny, a jednocześnie udostępnia przydatne funkcje za pośrednictwem publicznego interfejsu.
Otrzymasz trzy dane wejściowe:
- Początkowa kwota wpłaty (decimal)
- Kwota wypłaty, którą należy spróbować wypłacić (decimal)
- Kwota, dla której należy sprawdzić dostępność środków (decimal)
Wyświetl wynik w tym formacie:
Deposit {amount}: {True/False}
{GetStatement() result}
Withdraw {amount}: {True/False}
{GetStatement() result}
Can afford {amount}: {True/False}Na przykład, jeśli dane wejściowe to 100.50, 30.25 i 80.00, wynik powinien wyglądać następująco:
Deposit 100.50: True
Current balance: $100.50
Withdraw 30.25: True
Current balance: $70.25
Can afford 80.00: FalseNajważniejsza myśl jest taka, że zewnętrzny kod nigdy nie uzyskuje bezpośredniego dostępu do pola salda. Nawet sformatowany ciąg znaków z saldem jest zwracany przez metodę publiczną, która wewnętrznie korzysta z prywatnej metody pomocniczej. To właśnie ukrywanie danych — szczegóły implementacji są całkowicie niewidoczne, a sposób przechowywania lub formatowania salda możesz zmienić bez naruszania kodu korzystającego z portfela!
Spróbuj swoich sił
using System;
using Finance;
class Program
{
public static void Main(string[] args)
{
// Odczytaj wartości wejściowe
decimal depositAmount = Convert.ToDecimal(Console.ReadLine());
decimal withdrawAmount = Convert.ToDecimal(Console.ReadLine());
decimal checkAmount = Convert.ToDecimal(Console.ReadLine());
// TODO: Utwórz instancję Wallet
// TODO: Wpłać środki i wypisz wynik w formacie: "Deposit {amount}: {True/False}"
// TODO: Wypisz zestawienie za pomocą GetStatement()
// TODO: Wypłać środki i wypisz wynik w formacie: "Withdraw {amount}: {True/False}"
// TODO: Wypisz zestawienie za pomocą GetStatement()
// TODO: Sprawdź, czy wystarczy środków, i wypisz wynik w formacie: "Can afford {amount}: {True/False}"
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online