Ukrywanie informacji
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 34 z 110.
Ukrywanie informacji to zasada leżąca u podstaw enkapsulacji — idea, że klasa powinna ukrywać swoje wewnętrzne działanie i udostępniać tylko to, co niezbędne. Poznałeś już mechanizmy (pola prywatne, metody pobierające i ustawiające), ale prawdziwym celem jest projektowanie klas, które są łatwe w użyciu i trudne do niewłaściwego użycia.
Rozważ klasę, która zarządza hasłem użytkownika. Wewnętrzne przechowywanie i logika haszowania powinny być całkowicie ukryte:
class UserAccount {
String _username;
String _passwordHash;
UserAccount(this._username, String password)
: _passwordHash = _hashPassword(password);
static String _hashPassword(String password) {
return 'hashed_$password'; // Uproszczony przykład
}
bool verifyPassword(String password) {
return _passwordHash == _hashPassword(password);
}
String get username => _username;
}Zewnętrzny kod nigdy nie widzi skrótu hasła ani nie wie, jak jest on przechowywany. Po prostu wywołuje verifyPassword(). Dzięki temu rozdzieleniu możesz całkowicie zmienić algorytm haszowania bez wpływu na kod korzystający z tej klasy.
Kluczowa wskazówka: myśl o swojej klasie od zewnątrz do wewnątrz.
Zadaj sobie pytanie: „Czego tak naprawdę potrzebuje osoba korzystająca z tej klasy?”. Udostępniaj tylko to, a wszystko inne zachowaj jako prywatne. Dzięki temu powstaje wyraźna granica między tym, co robi twoja klasa (jej publicznym interfejsem), a tym, jak to robi (jej prywatną implementacją).
Wyzwanie
ŁatwyZbudujmy bezpieczny system portfela, który pokazuje w praktyce ukrywanie informacji. Utworzysz cyfrowy portfel zarządzający saldem użytkownika i historią transakcji, przy czym wszystkie wrażliwe szczegóły wewnętrzne będą całkowicie ukryte przed kodem zewnętrznym.
Podzielisz kod na dwa pliki:
wallet.dart: Utwórz klasęWallet, która bezpiecznie zarządza pieniędzmi. Portfel powinien publicznie przechowywać imię właściciela, ale wszystko inne powinno być ukryte za przejrzystym publicznym interfejsem:- Wewnętrzne saldo i rejestr transakcji powinny być prywatne — kod zewnętrzny nigdy nie powinien widzieć, jak przechowujesz pieniądze ani śledzisz transakcje
- Prywatna metoda powinna wewnętrznie obsługiwać rejestrowanie transakcji (przechowując wpisy takie jak
"Deposit: $50.0"lub"Withdrawal: $30.0") - Udostępnij publiczną metodę
deposit(double amount), która dodaje pieniądze (tylko jeśli kwota jest dodatnia) i wewnętrznie rejestruje transakcję - Udostępnij publiczną metodę
withdraw(double amount), która wypłaca pieniądze tylko wtedy, gdy dostępne środki są wystarczające, a kwota jest dodatnia, oraz wewnętrznie rejestruje transakcję. Zwróćtrue, jeśli operacja się powiedzie, w przeciwnym raziefalse - Udostępnij publiczny getter
balance, który zwraca bieżące saldo (dostęp tylko do odczytu) - Udostępnij publiczną metodę
getTransactionCount(), która zwraca liczbę zarejestrowanych transakcji (nie ujawniając rzeczywistej listy transakcji) - Udostępnij publiczną metodę
getLastTransaction(), która zwraca tekst ostatniej transakcji lub"No transactions", jeśli nie ma żadnych transakcji - Dodaj metodę
printSummary(), która wyświetla stan portfela
main.dart: Zaimportuj portfel i pokaż, jak ukrywanie informacji tworzy przejrzysty i bezpieczny interfejs:- Utwórz portfel dla właściciela
"Sarah" - Wpłać
100.0 - Wpłać
50.0 - Spróbuj wypłacić
200.0i wyświetl"Withdrawal of 200.0: [success/failed]"zależnie od wyniku - Wypłać
30.0i wyświetl"Withdrawal of 30.0: [success/failed]" - Wywołaj
printSummary() - Wyświetl
"Transaction count: [count]" - Wyświetl
"Last transaction: [transaction]"
- Utwórz portfel dla właściciela
Metoda printSummary() powinna wyświetlać:
=== Wallet Summary ===
Owner: [ownerName]
Balance: $[balance]Zwróć uwagę, że kod zewnętrzny może komunikować się z portfelem za pomocą przejrzystych, celowych metod, nie wiedząc przy tym, jak przechowywane jest saldo ani jak wewnętrznie śledzone są transakcje. Szczegóły implementacji są całkowicie ukryte — możesz zmienić sposób przechowywania transakcji bez wpływu na kod korzystający z portfela.
Oczekiwany wynik:
Withdrawal of 200.0: failed
Withdrawal of 30.0: success
=== Wallet Summary ===
Owner: Sarah
Balance: $120.0
Transaction count: 3
Last transaction: Withdrawal: $30.0Spróbuj swoich sił
import 'wallet.dart';
void main() {
// TODO: Utwórz portfel dla właścicielki "Sarah"
// TODO: Wpłać 100.0
// TODO: Wpłać 50.0
// TODO: Spróbuj wypłacić 200.0 i wyświetl wynik
// Format: "Withdrawal of 200.0: [success/failed]"
// TODO: Wypłać 30.0 i wyświetl wynik
// Format: "Withdrawal of 30.0: [success/failed]"
// TODO: Wywołaj printSummary()
// TODO: Wyświetl liczbę transakcji
// Format: "Transaction count: [count]"
// TODO: Wyświetl ostatnią transakcję
// Format: "Last transaction: [transaction]"
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatnościPoćwicz samodzielnie: Kompilator Dart online