Klasy sealed
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 22 z 70.
Czasami chcesz przerwać łańcuch dziedziczenia. Słowo kluczowe sealed uniemożliwia dziedziczenie po klasie, blokując jej implementację, tak aby żadna inna klasa nie mogła jej rozszerzyć.
public sealed class FinalDog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
// To spowodowałoby błąd kompilacji:
// public class SuperDog : FinalDog { }Dlaczego warto oznaczyć klasę jako zapieczętowaną? Bezpieczeństwo i integralność projektu to częste powody. Jeśli starannie zaprojektujesz klasę i nie chcesz, by ktoś zmieniał jej działanie przez dziedziczenie, zapieczętowanie jej gwarantuje, że Twoja implementacja pozostanie nienaruszona.
Możesz też zapieczętować pojedyncze metody zamiast całych klas. Uniemożliwia to ich dalsze nadpisywanie, a jednocześnie pozwala dziedziczyć po samej klasie:
public class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine("Woof!");
}
}
public class Puppy : Dog
{
// Nie można przesłonić Speak() — jest oznaczona jako sealed
// Można jednak dodawać nowe metody
public void Whimper()
{
Console.WriteLine("Whimper...");
}
}Zauważ, że sealed w przypadku metody musi być połączone z override. Możesz zapieczętować tylko metodę, która była już wirtualna w klasie nadrzędnej. Daje ci to precyzyjną kontrolę: pozwól na pewne dostosowania w hierarchii dziedziczenia, a następnie zablokuj je w określonym miejscu.
Wyzwanie
ŁatwyZbudujmy system przetwarzania płatności, który pokazuje, jak używać sealed, aby zablokować możliwość modyfikowania kluczowych implementacji. Utworzysz hierarchię, w której niektóre metody płatności można rozszerzać, ale te najbezpieczniejsze są zapieczętowane, aby zapobiec wszelkim modyfikacjom.
Utworzysz cztery pliki, aby uporządkować swój kod:
PaymentMethod.cs: Zdefiniuj klasęPaymentMethodw przestrzeni nazwPayments. Ta klasa bazowa ma właściwośćName(string) oraz konstruktor, który ją ustawia. Dodaj metodęvirtualo nazwieProcessPayment(decimal amount), która zwraca"Processing ${amount} via {Name}".CreditCardPayment.cs: Zdefiniuj klasęCreditCardPaymentw przestrzeni nazwPayments, która dziedziczy poPaymentMethod. Dodaj właściwośćCardNumber(string). Konstruktor przyjmuje nazwę i numer karty, używającbase, aby przekazać nazwę. Przesłoń metodęProcessPaymentz użyciem słowa kluczowegosealed, aby żadna klasa pochodna nie mogła zmienić tego działania. Metoda powinna zwracać"Charging ${amount} to card ending in {last 4 digits of CardNumber}".BankTransfer.cs: Zdefiniuj klasęsealedo nazwieBankTransferw przestrzeni nazwPayments, która dziedziczy poPaymentMethod. Cała ta klasa jest zapieczętowana — nikt nie może po niej dziedziczyć. Dodaj właściwośćBankName(string). Konstruktor przyjmuje nazwę i nazwę banku. Przesłoń metodęProcessPayment, aby zwracała"Transferring ${amount} from {BankName}".Program.cs: W pliku głównym utwórz instancje wszystkich trzech typów płatności, używając wartości wejściowych. Zapisz je jako referencje typuPaymentMethodi wywołaj dla każdej metodęProcessPayment, aby pokazać polimorfizm z zapieczętowanymi implementacjami.
Otrzymasz trzy dane wejściowe:
- Numer karty kredytowej (16 cyfr)
- Nazwa banku
- Kwota płatności
Wypisz wynik przetworzenia płatności każdą z metod:
{base PaymentMethod result}
{CreditCardPayment result}
{BankTransfer result}Na przykład, jeśli danymi wejściowymi są 1234567890123456, First National i 150.00, wynik powinien być następujący:
Processing $150.00 via Generic
Charging $150.00 to card ending in 3456
Transferring $150.00 from First NationalUżyj "Generic" jako nazwy instancji bazowej klasy PaymentMethod, "Credit Card" dla płatności kartą kredytową oraz "Bank Transfer" dla przelewu bankowego.
Zapieczętowana metoda w CreditCardPayment gwarantuje, że jeśli ktoś utworzy klasę pochodną, nie będzie mógł zmienić sposobu przetwarzania płatności kartą kredytową. Zapieczętowana klasa BankTransfer całkowicie uniemożliwia dziedziczenie — oba te rozwiązania są ważnymi wzorcami bezpieczeństwa w systemach płatności!
Spróbuj swoich sił
using System;
using Payments;
class Program
{
public static void Main(string[] args)
{
// Wczytaj dane wejściowe
string cardNumber = Console.ReadLine();
string bankName = Console.ReadLine();
decimal amount = Convert.ToDecimal(Console.ReadLine());
// TODO: Utwórz instancję bazowej klasy PaymentMethod o nazwie "Generic"
// Przechowuj ją w referencji typu PaymentMethod
// TODO: Utwórz instancję CreditCardPayment o nazwie "Credit Card" i podanym numerze karty
// Przechowuj ją w referencji typu PaymentMethod
// TODO: Utwórz instancję BankTransfer o nazwie "Bank Transfer" i podanej nazwie banku
// Przechowuj ją w referencji typu PaymentMethod
// TODO: Wywołaj ProcessPayment dla każdej z nich i wypisz wyniki
// Każdy wynik powinien znaleźć się w osobnym wierszu
}
}
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