INF.04 · C# · lekcja 15
Klasy abstrakcyjne i interfejsy
Oddziel wymaganie od implementacji
- Zrozum
- Sprawdź
- Napisz sam
Po tej lekcji: oddziel wymaganie od implementacji
Dzisiaj zrobisz konkretną rzecz
Klasy abstrakcyjne i interfejsy to narzędzia PROJEKTOWANIA dobrego kodu — wymuszają na programistach (także na Tobie samym za kilka miesięcy) konsekwentne implementowanie wymaganych zachowań, zamiast polegać na pamięci czy dokumentacji.
Umowa o pracę na stanowisku "kierowca" wymaga posiadania prawa jazdy, niezależnie od tego, czy ktoś jest kierowcą autobusu, ciężarówki czy taksówki — to jak interfejs: opisuje wymaganą "umiejętność", nie mówiąc, JAK dokładnie ma być zrealizowana.
Sprawdź w działaniu
Oddziel wymaganie od implementacji
Najpierw przewidź wynik. Zmień dane w modelu i porównaj efekt. Spróbuj także przypadku granicznego, a nie tylko pierwszego poprawnego przykładu.
Model pokazuje wybraną zasadę. Nie uruchamia ani nie ocenia wklejonego kodu C#.
1. Przygotuj i uruchom własny projekt
2. Najpierw mały przykład
Wykonaj tę próbę przed dłuższym rozwiązaniem. interface opisuje wymagane operacje. Film : IOpis zobowiązuje klasę do udostępnienia publicznej metody o wskazanej sygnaturze. Klasa abstrakcyjna może dodatkowo przechowywać wspólny stan i implementację.
IOpis obiekt = new Film();
Console.WriteLine(obiekt.Opisz());
interface IOpis { string Opisz(); }
class Film : IOpis
{
public string Opisz() => "Film do wypożyczenia";
}
Film do wypożyczenia
- Co sprawdzić? Zmień jedną wartość i przed uruchomieniem zapisz przewidywany rezultat. Porównaj go z opisem wyniku; przy błędzie wróć do konkretnej instrukcji.
Jak to działa — 1
W Lekcji 14 klasa Pracownik miała sensowną, domyślną wersję ObliczPremie() (5%). Czasem jednak klasa bazowa reprezentuje pojęcie zbyt OGÓLNE, żeby mieć jakąkolwiek sensowną implementację metody — wtedy używa się klasy abstrakcyjnej.
Klasa abstrakcyjna (abstract class) NIE MOŻE być bezpośrednio "zmaterializowana" przez new — służy WYŁĄCZNIE jako baza do dziedziczenia. Może zawierać zarówno zwykłe metody (ze wspólną implementacją), jak i metody abstrakcyjne (abstract) — bez ciała, WYMUSZAJĄCE, żeby każda klasa pochodna sama je zaimplementowała:
abstract class Ksztalt { public abstract double ObliczPole(); }
Klasa Ksztalt nie wie, jak obliczyć pole — to zależy od konkretnego kształtu, więc każda klasa pochodna (Kolo, Kwadrat) MUSI dostarczyć własną implementację ObliczPole(), inaczej program się nie skompiluje.
Interfejs (interface) idzie o krok dalej — to CZYSTA umowa, opisująca, JAKIE metody musi mieć klasa, całkowicie bez żadnej implementacji. Klasa może zaimplementować DOWOLNIE WIELE interfejsów (w przeciwieństwie do dziedziczenia z jednej klasy bazowej):
interface IPorownywalny { int Porownaj(object inny); }
class Produkt : IPorownywalny { public int Porownaj(object inny) { ... } }
Jak to działa — 2
Kiedy klasa abstrakcyjna, a kiedy interfejs? Klasa abstrakcyjna, gdy klasy pochodne mają WSPÓLNĄ implementację części zachowania (np. wszystkie kształty mają pole Nazwa). Interfejs, gdy chcesz tylko WYMUSIĆ, że dana klasa "potrafi" coś zrobić, niezależnie od tego, z czego dziedziczy (np. "da się porównać", "da się zapisać do pliku") — klasa może "potrafić" wiele różnych rzeczy naraz.
3. Pełny przykład — czytaj od Main
Teraz zastąp Program.cs tym programem. Zanim uruchomisz, znajdź dane wejściowe, operację i miejsce prezentacji wyniku. W tym wariancie zachowujemy jawny Main. Nie wklejaj kilku pełnych programów do jednego pliku.
using System;
abstract class Ksztalt
{
public string Nazwa;
public Ksztalt(string nazwa)
{
Nazwa = nazwa;
}
public abstract double ObliczPole(); // BEZ implementacji - wymusza nadpisanie
public void WyswietlPole() // zwykła metoda, wspólna dla wszystkich kształtów
{
Console.WriteLine(Nazwa + ": pole = " + ObliczPole());
}
}
class Kolo : Ksztalt
{
public double Promien;
public Kolo(double promien) : base("Koło")
{
Promien = promien;
}
public override double ObliczPole()
{
return Math.PI * Promien * Promien;
}
}
class Kwadrat : Ksztalt
{
public double Bok;
public Kwadrat(double bok) : base("Kwadrat")
{
Bok = bok;
}
public override double ObliczPole()
{
return Bok * Bok;
}
}
class Program
{
static void Main()
{
// Ksztalt ksztalt = new Ksztalt("x"); // BŁĄD - nie można utworzyć klasy abstrakcyjnej
Ksztalt[] figury = { new Kolo(3), new Kwadrat(4) };
foreach (Ksztalt f in figury)
{
f.WyswietlPole();
}
}
}
- Prześledź kod Próba
new Ksztalt("x")zgłosiłaby błąd kompilacji — klasa abstrakcyjna istnieje TYLKO po to, żeby po niej dziedziczyć, nigdy żeby ją bezpośrednio tworzyć. MetodaWyswietlPole()jest zwykłą (nie-abstrakcyjną) metodą klasyKsztalti działa identycznie dla wszystkich kształtów, WEWNĄTRZ wywołującObliczPole()— dzięki polimorfizmowi (Lekcja 14) automatycznie uruchomi się właściwa, nadpisana wersja dla danego typu.
Sprawdź, zanim przepiszesz cały program
Częsty błąd to próba utworzenia obiektu klasy abstrakcyjnej przez new — zawsze zakończy się błędem kompilacji. Drugi błąd: zapomnienie implementacji którejś z metod abstrakcyjnych w klasie pochodnej — kod się nie skompiluje, dopóki WSZYSTKIE metody abstrakcyjne nie zostaną nadpisane. Trzeci: mylenie interfejsu z klasą abstrakcyjną — interfejs nigdy nie zawiera żadnej implementacji (tylko "nagłówki" metod), a klasa abstrakcyjna MOŻE mieszać metody z implementacją i bez niej.
Ćwiczenie z pomocą · samodzielna praca
4. Teraz Twoja zmiana
Dodaj do przykładu z lekcji trzecią klasę pochodną Trojkat (pola: podstawa, wysokość) implementującą własną wersję ObliczPole().
Spełnij warunki polecenia. Zapisz wejście, otrzymany wynik i wyjaśnij, która część kodu za niego odpowiada.
? Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź
Wróć do podstawowej formy oraz objaśnienia: interface opisuje wymagane operacje. Film : IOpis zobowiązuje klasę do udostępnienia publicznej metody o wskazanej sygnaturze. Klasa abstrakcyjna może dodatkowo przechowywać wspólny stan i implementację.
Krok 2 — pokaż podpowiedź
Zmień tylko część potrzebną do polecenia. Zachowaj działającą wersję przed zmianą i uruchamiaj po każdym małym etapie.
Krok 3 — pokaż podpowiedź
Sprawdź przypadek zwykły i graniczny; porównaj z regułą: Film do wypożyczenia
Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.
5. Sprawdź rozumienie, nie tylko uruchomienie
Dalej na swoim koncie
Rozgrzewki, quiz i zadania są w biurku ucznia.
Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.
Zaloguj się →