Interfejs a klasa abstrakcyjna
Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 30 z 91.
Skoro znasz już interfejsy i klasy abstrakcyjne, wyjaśnijmy, kiedy używać każdego z tych rozwiązań. Choć mogą wydawać się podobne, służą różnym celom i mają różne możliwości.
| Cecha | Interfejs | Klasa abstrakcyjna |
|---|---|---|
| Metody | Tylko sygnatury metod | Metody abstrakcyjne i konkretne |
| Właściwości | Tylko stałe | Może mieć właściwości |
| Wielokrotne dziedziczenie | Klasa może implementować wiele interfejsów | Klasa może rozszerzać tylko jedną klasę |
| Konstruktor | Może deklarować sygnaturę __construct (bez ciała) | Może definiować konstruktor z ciałem |
Użyj interfejsu, gdy chcesz zdefiniować kontrakt, który mogą spełniać niepowiązane ze sobą klasy. Na przykład zarówno Printer, jak i Logger mogą implementować interfejs Writable, mimo że nie mają wspólnego przodka.
Użyj klasy abstrakcyjnej, gdy masz powiązane klasy, które współdzielą wspólny kod. Jeśli klasy Dog i Cat potrzebują tej samej implementacji metody eat(), abstrakcyjna klasa Animal pozwala napisać ten kod tylko raz.
Możesz także połączyć oba podejścia. Klasa może rozszerzać klasę abstrakcyjną, implementując jednocześnie wiele interfejsów:
<?php
abstract class Animal {
protected $name;
public function eat() {
return $this->name . " is eating";
}
}
interface Swimmable {
public function swim();
}
class Duck extends Animal implements Swimmable {
public function __construct($name) {
$this->name = $name;
}
public function swim() {
return $this->name . " is swimming";
}
}
Kluczowa kwestia: Wybieraj interfejsy, aby definiować możliwości w niepowiązanych ze sobą klasach. Wybieraj klasy abstrakcyjne, gdy chcesz współdzielić kod między powiązanymi klasami.
Wyzwanie
ŁatwyZbudujmy system pojazdów, który pokazuje, kiedy używać klasy abstrakcyjnej, a kiedy interfejsu oraz jak skutecznie łączyć oba podejścia.
Utworzysz system, w którym pojazdy współdzielą wspólne właściwości dzięki dziedziczeniu, ale mogą też zyskiwać dodatkowe możliwości dzięki interfejsom. Odzwierciedla to rzeczywiste decyzje projektowe: samochód "is a" pojazd (dziedziczenie), ale także "has the capability" do tankowania (interfejs).
Rozmieść kod w czterech plikach:
Refuelable.php: Zdefiniuj interfejsRefuelablez jedną sygnaturą metody:refuel($amount). Ta możliwość może dotyczyć każdej klasy wymagającej tankowania, niezależnie od jej hierarchii dziedziczenia.Vehicle.php: Zdefiniuj abstrakcyjną klasęVehicle, która udostępnia wspólną funkcjonalność wszystkim pojazdom. Powinna zawierać chronioną właściwość$brandoraz chronioną właściwość$fuelLevel(początkowa wartość to 0). Konstruktor przyjmuje nazwę marki. Dodaj konkretną metodęgetBrand(), która zwraca markę, konkretną metodęgetFuelLevel(), która zwraca aktualny poziom paliwa, oraz abstrakcyjną metodędrive(), którą muszą zaimplementować klasy potomne.Car.php: Utwórz klasęCar, która rozszerza klasęVehiclei implementuje interfejsRefuelable. Na początku dołącz oba wymagane pliki. Konstruktor przyjmuje markę i przekazuje ją do konstruktora klasy nadrzędnej. Zaimplementuj metodędrive(), aby zwracała"[brand] car is driving". Zaimplementuj metodęrefuel($amount), aby dodawała podaną wartość do poziomu paliwa i zwracała"[brand] refueled to [fuelLevel] liters".main.php: Dołącz plik Car. Otrzymasz dwa dane wejściowe: nazwę marki i ilość paliwa (przekonwertuj ją na liczbę całkowitą). Utwórz obiektCarz podaną marką. Wypisz wynik metodydrive()w pierwszym wierszu, a następnie w drugim wierszu wypisz wynik metodyrefuel()z podaną ilością paliwa.
To wyzwanie pokazuje kluczową różnicę: abstrakcyjna klasa Vehicle współdzieli kod i ustanawia relację "is-a", natomiast interfejs Refuelable dodaje możliwość, która może dotyczyć innych, niezwiązanych klas (takich jak generator czy łódź), bez konieczności, by były pojazdami.
Spróbuj swoich sił
<?php
require_once 'Car.php';
// Odczytaj dane wejściowe
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));
// TODO: Utwórz Car z podaną marką
// TODO: Wypisz wynik drive() w pierwszym wierszu
// TODO: Wypisz wynik refuel() z podaną ilością paliwa w drugim 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ętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe $thisMetodyWłaściwościKonstruktor (__construct)Destruktor (__destruct)Powtórka — prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe parent::Przesłanianie metodSłowo kluczowe finalKlasy abstrakcyjnePowtórzenie – hierarchia pracowników7Enkapsulacja
Public, protected, privateModyfikatory dostępu w szczegółachGettery i setteryUkrywanie informacjiPromocja parametrów konstruktora (8.0)Podsumowanie — system ewidencji studentów2Przestrzenie nazw i autoloading
Wprowadzenie do przestrzeni nazwSłowo kluczowe useStandard autoloadingu PSR-4Autoloader ComposeraPodsumowanie – uporządkowany projekt5Interfejsy i kontrakty
Wprowadzenie do interfejsówImplementowanie interfejsówImplementowanie wielu interfejsówInterfejs a klasa abstrakcyjnaPodpowiadanie typów za pomocą interfejsówPodsumowanie — kalkulator kształtów8Metody magiczne
Wprowadzenie do metod magicznych__toString i __debugInfo__get, __set, __isset, __unset__call i __callStatic__clone i klonowanie obiektów__serialize i __unserializePodsumowanie — własna kolekcja11System typów i obsługa błędów
Deklaracje typówTypy nullableTypy sumy i przecięciaKlasy wyjątkówNiestandardowa hierarchia wyjątkówTry, Catch, FinallyPodsumowanie — walidator formularzy14Projekt: Zarządzanie biblioteką
Przegląd projektuKlasy książki i użytkownika3Właściwości klas
Właściwości instancji a właściwości statyczneStałe w klasachMetody i właściwości statyczneWłaściwości prywatne i chronioneWłaściwości tylko do odczytu (PHP 8.1)Podsumowanie — menedżer kont bankowych6Polimorfizm
Ponowne omówienie nadpisywania metodPolimorfizm za pomocą interfejsówPodpowiedzi typów i typy sumyPóźne wiązanie statycznePodsumowanie — procesor płatności9Cechy
Wprowadzenie do cechKorzystanie z wielu cechRozwiązywanie konfliktów cechMetody abstrakcyjne w cechachCechy a dziedziczenie12Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec StrategyPoćwicz samodzielnie: Kompilator PHP online