Menu
Coddy logo textTech

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
end

Zwróć 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: Toyota

Ten 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.

challenge icon

Wyzwanie

Łatwy

Zbudujmy 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 __index i 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ć pole wingspan do 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:

  1. Nazwa ptaka
  2. 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 200

Najważ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)
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Lua online