Znasz już podstawy programowania obiektowego (OOP). Wiesz, czym są klasy, obiekty i dziedziczenie. Czas poznać dwa potężne narzędzia, które pozwolą Ci pisać kod bardziej elastyczny, uporządkowany i profesjonalny: klasy abstrakcyjne oraz interfejsy.
Wyobraź sobie, że jesteś głównym architektem wielkiego projektu. Nie musisz budować każdego detalu własnoręcznie, ale musisz ustalić twarde zasady dla swojej ekipy budowlanej. Klasy abstrakcyjne i interfejsy to właśnie takie zasady i szkice koncepcyjne.
1. Klasy abstrakcyjne: Niedokończony szkic
Klasa abstrakcyjna to taka klasa, która służy jako fundament dla innych klas, ale jest na tyle ogólna, że nie można stworzyć jej bezpośredniego obiektu (instancji).
Wyobraź sobie pojęcie "Zwierzę". Czy widziałeś kiedyś na ulicy po prostu "zwierzę"? Nie! Widziałeś psa, kota, gołębia. "Zwierzę" to tylko kategoria, abstrakcyjna idea.
Najważniejsze cechy klasy abstrakcyjnej:
- Zakaz tworzenia obiektów: Nie możesz napisać
Zwierze z = new Zwierze();. Zakończy się to błędem. - Metody zaimplementowane: Może zawierać zwykłe metody z gotowym kodem (np.
oddychaj(), bo każde zwierzę oddycha tak samo). - Metody abstrakcyjne: Może zawierać metody bez ciała (tylko nazwę i parametry), np.
dajGlos(). Zmusza to każdą klasę potomną (np.Pies) do napisania własnej wersji tej metody. - Pola (zmienne): Może przechowywać stan, np.
waga,wiek.
Przykład w Java:
abstract class Zwierze {
int wiek; // Klasa abstrakcyjna może mieć zmienne
// Zwykła metoda - wspólna dla wszystkich
void oddychaj() {
System.out.println("Wdech i wydech...");
}
// Metoda abstrakcyjna - każda podklasa musi to zrobić po swojemu
abstract void dajGlos();
}
class Pies extends Zwierze {
void dajGlos() {
System.out.println("Hau hau!");
}
}
2. Interfejsy: Żelazny kontrakt
Jeśli klasa abstrakcyjna to niedokończony szkic tożsamości, to interfejs jest wyłącznie kontraktem określającym umiejętności (co obiekt potrafi zrobić, ale bez wnikania, jak to robi).
Wyobraź sobie zdolność "Latania". Latać może ptak (zwierzę), samolot (maszyna) i Superman (kosmita). Nie mają one ze sobą żadnego wspólnego przodka, ale łączy je ta sama umiejętność. Właśnie do tego służą interfejsy.
Najważniejsze cechy interfejsu:
- Czysty kontrakt: Tradycyjnie interfejs zawiera tylko sygnatury metod (choć w nowszych wersjach języków, jak Java czy C#, pojawiły się metody domyślne).
- Brak stanu: Interfejs nie przechowuje zmiennych obiektu (może mieć tylko stałe).
- Wielodziedziczenie: To najważniejsza różnica! W większości języków klasa może dziedziczyć tylko po jednej klasie, ale może implementować wiele interfejsów jednocześnie (np. Smartfon może implementować interfejsy
Aparat,TelefoniOdtwarzaczMuzyki).
Przykład:
interface Latajacy {
void lec(); // Tylko obietnica, że coś potrafi latać
}
class Samolot implements Latajacy {
void lec() {
System.out.println("Uruchamiam silniki odrzutowe i wzbijam się w powietrze!");
}
}
class Ptak extends Zwierze implements Latajacy {
void dajGlos() {
System.out.println("Ćwir ćwir!");
}
void lec() {
System.out.println("Macham skrzydłami!");
}
}
3. Porównanie: Co wybrać?
Często programiści zastanawiają się, którego narzędzia użyć. Poniższa tabela pomoże Ci usystematyzować wiedzę:
| Cecha | Klasa abstrakcyjna | Interfejs |
| Relacja (Związek) | Definiuje tożsamość: "jest czymś" (Pies jest Zwierzęciem). | Definiuje rolę/umiejętność: "potrafi coś" (Ptak potrafi latać). |
| Dziedziczenie | Można dziedziczyć tylko po jednej klasie. | Można implementować wiele interfejsów na raz. |
| Zmienne (Stan) | Może posiadać dowolne zmienne. | Tylko stałe (np. public static final). |
| Konstruktor | Może posiadać konstruktor (do inicjalizacji swoich pól). | Nie posiada konstruktora. |
| Modyfikatory dostępu | Metody mogą być public, protected, private. |
Zazwyczaj wszystkie metody są domyślnie public. |
💡 Złota zasada z podręcznika: > Używaj klas abstrakcyjnych, gdy masz grupę blisko spokrewnionych klas, które współdzielą dużo kodu i wspólny "stan" (zmienne).
Używaj interfejsów, gdy chcesz nadać specyficzną umiejętność klasom, które nie są ze sobą w żaden sposób spokrewnione, lub gdy zależy Ci na wielokrotnym dziedziczeniu cech.
Podsumowanie
Klasy abstrakcyjne i interfejsy nie służą do tego, aby Twój kod po prostu "działał". Służą do tego, by Twój kod był uporządkowany, bezpieczny i łatwy do rozbudowy w przyszłości. Dzięki nim narzucasz zasady i wymagasz określonego zachowania od klas potomnych, unikając przy tym powtarzania tego samego kodu. Powodzenia w dalszej nauce!