Rozszerzanie konstruktora
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 33 z 70.
Gdy klasa potomna dziedziczy po klasie nadrzędnej, często chcesz, aby jej konstruktor inicjalizował wszystko to, co konstruktor klasy nadrzędnej, a także dodatkowe pola właściwe dla klasy potomnej. Zamiast powielać kod, możesz bezpośrednio wywołać konstruktor klasy nadrzędnej.
Ta technika polega na użyciu nazwy klasy nadrzędnej do wywołania jej metody :new() i przekazaniu self jako pierwszego argumentu. Tworzy to obiekt bazowy ze wszystkimi elementami inicjalizacji klasy nadrzędnej, który następnie dostosowujesz do klasy potomnej.
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
-- Car dziedziczy po Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- Wywołaj konstruktor klasy bazowej
obj.brand = brand -- Dodaj pole specyficzne dla klasy potomnej
setmetatable(obj, Car) -- Powiąż ponownie z Car
return obj
endZwróć uwagę na najważniejsze kroki w konstruktorze klasy potomnej: najpierw wywołujemy Vehicle.new(Vehicle, speed), aby uzyskać obiekt z już ustawionym polem speed. Następnie dodajemy pole brand właściwe dla klasy potomnej. Na koniec zmieniamy metatabelę z Vehicle na Car, aby instancja najpierw wyszukiwała metody w klasie potomnej.
Podczas wywoływania konstruktora klasy nadrzędnej możesz też użyć składni z dwukropkiem: Vehicle:new(speed) jest równoważne Vehicle.new(Vehicle, speed), ponieważ w obu przypadkach Vehicle jest przekazywane jako pierwszy argument. Obie formy działają tu poprawnie, więc możesz użyć tej, która jest dla Ciebie bardziej czytelna.
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- Wynik: 120
print(myCar.brand) -- Wynik: ToyotaTen wzorzec sprawia, że Twój kod jest zgodny z zasadą DRY: współdzielona logika inicjalizacji znajduje się w klasie nadrzędnej, a każde dziecko obsługuje tylko swoje unikalne atrybuty.
Wyzwanie
ŁatwyZbudujmy hierarchię stworzeń, w której klasa potomna rozszerza konstruktor klasy nadrzędnej! Utworzysz klasę nadrzędną Animal i klasę potomną Bird. Konstruktor klasy Bird wywoła konstruktor klasy Animal, aby obsłużyć wspólną inicjalizację, zanim doda własny, unikalny atrybut.
Rozmieścisz swój kod w trzech plikach:
Animal.lua: Zdefiniuj klasę nadrzędną z konstruktorem:new(name), który zapisuje nazwę zwierzęcia. Dodaj metodę:describe(), która wypisuje nazwę zwierzęcia. Skonfiguruj standardowy wzorzec klasy z użyciem__indexi zwróć klasę.Bird.lua: Zdefiniuj klasę potomną dziedziczącą po Animal. Konstruktor:new(name, wingspan)klasy Bird powinien wywołać konstruktor klasy nadrzędnej za pomocąAnimal.new(Animal, name), aby zainicjalizować nazwę, następnie dodać polewingspando obiektu, a na końcu ponownie powiązać metatabelę z Bird. Dodaj metodę:info(), która wypisuje zarówno nazwę, jak i rozpiętość skrzydeł w określonym formacie.main.lua: Załaduj moduł Bird, odczytaj dwa wejścia (nazwę i rozpiętość skrzydeł), utwórz obiekt Bird i wywołaj obie metody, aby pokazać, że odziedziczona funkcjonalność i funkcjonalność specyficzna dla klasy potomnej działają razem.
Otrzymasz dwa wejścia:
- Nazwa ptaka
- Rozpiętość skrzydeł ptaka (liczba)
W pliku głównym utwórz obiekt Bird z podaną nazwą i rozpiętością skrzydeł. Następnie wywołaj :describe(), a po nim :info(), każdą metodę w osobnym wierszu.
Metoda :describe() (odziedziczona po Animal) powinna wypisać:
Animal: {name}Metoda :info() (zdefiniowana w Bird) powinna wypisać:
{name} has a wingspan of {wingspan}Na przykład, jeśli wejściami są Eagle i 200, wynikiem powinno być:
Animal: Eagle
Eagle has a wingspan of 200Najważniejsze jest to, że konstruktor Bird nie powiela inicjalizacji nazwy. Przekazuje to zadanie konstruktorowi klasy Animal, a następnie dodaje tylko to, co jest unikalne dla ptaków. Dzięki temu kod pozostaje zgodny z zasadą DRY, a jednocześnie tworzy właściwy łańcuch dziedziczenia!
Spróbuj swoich sił
-- main.lua: Punkt wejścia
local Bird = require('Bird')
-- Wczytaj dane wejściowe
local name = io.read()
local wingspan = tonumber(io.read())
-- TODO: Utwórz instancję Bird o podanych nazwie i rozpiętości skrzydeł
-- TODO: Wywołaj :describe() dla ptaka (odziedziczoną po Animal)
-- TODO: Wywołaj :info() dla ptaka (zdefiniowaną w Bird)
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Koncepcja „self”
Tabele z funkcjamiJawne „self”Składnia z dwukropkiemKropka czy dwukropekPodsumowanie — poruszający się punkt4Projekt: Bank cyfrowy
Konfiguracja projektuMetoda wpłaty7Polimorfizm i nadpisywanie
Nadpisywanie metodWywoływanie metod klasy nadrzędnejTypowanie kaczeWspólny interfejsSprawdzanie typuPodsumowanie — role pracowników10Projekt: Menedżer kształtów
Konfiguracja projektuKlasa prostokątaKlasa kołaMetoda obwoduKolekcja kształtówPole całkowiteFiltrowanie kształtów2Wzorzec prototypu klasy
Koncepcja prototypuŁączenie za pomocą __indexKonstruktor :new()Inicjalizacja atrybutówNiezależne instancjePodsumowanie – fabryka samochodów5Przeciążanie operatorów w OOP
Dodawanie obiektówOdejmowanie obiektówŁączenie obiektówPorównywanie obiektów (<, >)Powtórka — działania na portfelu8Hermetyzacja
Konwencje nazewnictwaDomknięcia dla prywatnościDostęp za pomocą domknięćTabele tylko do odczytuLogika walidacjiPodsumowanie — bezpieczny sejf11Wzorce projektowe (podstawy)
Funkcje fabrykująceTabela SingletonWzorzec iteratoraObserwator (nasłuchujący)Powtórka — fabryka loggera3Stan i zachowanie obiektu
Zmienne instancjiMetody dostępoweMetody ustawiająceWłaściwości obliczaneFormatowanie ciągów znakówSprawdzanie równościPodsumowanie – ocena ucznia6Podstawy dziedziczenia
Konfiguracja dziedziczeniaDziedziczenie metodRozszerzanie konstruktoraDodawanie metod potomnychWspólne i unikalne elementyPowtórka — hierarchia kształtówPoćwicz samodzielnie: Kompilator Lua online