Mixiny a dziedziczenie
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 53 z 70.
Poznałeś dwa sposoby współdzielenia zachowania: dziedziczenie tworzy relację „jest czymś” za pośrednictwem łańcucha prototypów, a domieszki kopiują funkcje bezpośrednio do klasy. Kiedy jednak warto zastosować każde z tych podejść?
Używaj dziedziczenia, gdy obiekty mają wspólną, fundamentalną tożsamość. Bird jest Animal. Ma sens, aby Bird dziedziczył podstawowe zachowania zwierzęcia, takie jak :eat() czy :sleep(). Ta relacja jest trwała i określa, czym obiekt jest w swojej istocie.
Używaj mixins, gdy chcesz dodać możliwość, która nie definiuje podstawowej tożsamości obiektu.
Nie wszystkie ptaki potrafią latać: pingwiny i strusie to ptaki, ale nie latają. Latanie to umiejętność, a nie cecha definiująca ptaka. Mixin Flyable pozwala dodać możliwość latania ptakom, które jej potrzebują, bez narzucania jej wszystkim.
Te dwa podejścia współdziałają. Tutaj zarówno Eagle, jak i Penguin dziedziczą po Bird, a tylko Eagle otrzymuje domieszkę:
-- Bird dziedziczy po Animal (relacja „jest”)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Eagle i Penguin są ptakami
local Eagle = setmetatable({}, {__index = Bird})
Eagle.__index = Eagle
local Penguin = setmetatable({}, {__index = Bird})
Penguin.__index = Penguin
-- Flyable to zdolność (miksin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Zastosuj miksin tylko do ptaków, które latają
applyMixin(Eagle, Flyable)Orzeł może wywołać :fly(). Pingwin nie może: fly nie występuje w Penguin, Bird ani Animal, więc penguin:fly() powoduje błąd. Zastosowanie miksy do Bird skopiowałoby natomiast fly do każdego ptaka, w tym pingwinów.
Wyzwanie
ŁatwyZbudujmy mały ekosystem stworzeń, który pokaże, kiedy używać dziedziczenia, a kiedy domieszek! Utworzysz klasę bazową Animal, klasę Bird, która po niej dziedziczy, oraz domieszkę Swimmable, ponieważ chociaż wszystkie ptaki są zwierzętami, nie wszystkie potrafią pływać.
Rozmieścisz swój kod w czterech plikach:
Animal.lua: Utwórz bazową klasę Animal z konstruktorem:new(name), który zapisuje nazwę. Dodaj metodę:eat(), która wypisuje{name} is eating. Reprezentuje ona podstawową tożsamość wspólną dla wszystkich zwierząt.Swimmable.lua: Utwórz domieszkę z metodą:swim(), która wypisuje{self.name} is swimming. Jest to zdolność, której nie mają wszystkie stworzenia, więc nie powinna być częścią łańcucha dziedziczenia.Bird.lua: Utwórz klasę Bird, która dziedziczy po Animal. Konstruktor:new(name)powinien wywoływać konstruktor klasy nadrzędnej. Dodaj metodę:chirp(), która wypisuje{name} chirps!. To zachowanie charakterystyczne dla ptaków. Nie umieszczaj pływania w klasie Bird: nie każdy ptak potrafi pływać.main.lua: Napisz tutaj funkcję pomocnicząapplyMixin(target, mixin): kopiuje ona wszystkie funkcje z domieszki do obiektu docelowego. Odczytaj imię ptaka i informację, czy potrafi pływać (yeslubno). Utwórz obiekt Bird i, jeśli potrafi pływać, zastosuj domieszkę Swimmable tylko do tego ptaka. Następnie wywołaj:eat()(odziedziczoną po Animal) oraz:chirp()(własną metodę Bird), a:swim()wywołaj tylko wtedy, gdy ten ptak ją ma (if bird.swim then).
Otrzymasz dwa wejścia:
- Imię ptaka (ciąg znaków)
- Informacja, czy ptak potrafi pływać:
yeslubno
Jeśli ptak potrafi pływać, wynik powinien być następujący:
{name} is eating
{name} chirps!
{name} is swimmingJeśli ptak nie potrafi pływać, wynik powinien być następujący:
{name} is eating
{name} chirps!Na przykład, jeśli wejściami są Duck i yes, wynikiem powinno być:
Duck is eating
Duck chirps!
Duck is swimmingJeśli wejściami są Sparrow i no, wynikiem powinno być:
Sparrow is eating
Sparrow chirps!To wyzwanie pokazuje kluczową różnicę: Bird jest Animal (dziedziczenie), ale pływanie to opcjonalna zdolność (domieszka). Ponieważ domieszka trafia tylko do ptaków, które potrafią pływać, ptak, który nie potrafi pływać, w ogóle nie ma metody swim.
Spróbuj swoich sił
-- main.lua: Punkt wejścia — utwórz ptaka i zademonstruj jego umiejętności
-- Funkcja pomocnicza do zastosowania mixina do klasy lub pojedynczego obiektu
local function applyMixin(target, mixin)
-- TODO: Skopiuj każdą funkcję z mixin do target
end
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Odczytaj dane wejściowe
local name = io.read()
local canSwimInput = io.read()
-- TODO: Utwórz instancję Bird o podanej nazwie
-- TODO: Jeśli wartością wejściową jest "yes", zastosuj mixin Swimmable tylko do tego ptaka
-- TODO: Wywołaj eat() — metodę odziedziczoną po Animal
-- TODO: Wywołaj chirp() — własną metodę Bird
-- TODO: Wywołaj swim() tylko wtedy, gdy ten ptak ma tę metodę
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ów9Kompozycja i mixiny
Relacja Has-ADelegowanieProste mixinyStosowanie wielu mixinówMixiny a dziedziczeniePodsumowanie — składanie robotaPoćwicz samodzielnie: Kompilator Lua online