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” za pośrednictwem łańcucha prototypów, a domieszki kopiują funkcje bezpośrednio do klasy. Kiedy jednak należy stosować każde z tych podejść?
Używaj dziedziczenia, gdy obiekty mają wspólną podstawową tożsamość. Bird jest Animal. Ma sens, aby Bird dziedziczył podstawowe zachowania zwierząt, takie jak :eat() lub :sleep(). Ta relacja jest trwała i określa, czym obiekt zasadniczo jest.
Używaj domieszek, 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 zdolność, a nie cecha definiująca ptaka. Mixin Flyable pozwala dodać możliwość latania ptakom, które jej potrzebują, bez narzucania jej wszystkim.
Dwa podejścia świetnie ze sobą współgrają:
-- Bird dziedziczy po Animal (relacja typu „jest”)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable to dodatkowa funkcjonalność (mixin)
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- Zastosuj mixin tylko do ptaków, które potrafią latać
applyMixin(Bird, Flyable)Ten wzorzec daje ci to, co najlepsze z obu światów: przejrzystą hierarchię klas, która zapewnia wspólną tożsamość, oraz elastyczne możliwości, które można dodawać w razie potrzeby.
Wyzwanie
ŁatwyStwórzmy mały ekosystem stworzeń, który pokaże, kiedy używać dziedziczenia, a kiedy miksinów! Utworzysz klasę bazową Animal, klasę Bird, która po niej dziedziczy, oraz miksin Swimmable, ponieważ wszystkie ptaki są zwierzętami, ale nie wszystkie ptaki 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 imię. Dodaj metodę:eat(), która wypisuje{name} is eating. Reprezentuje ona podstawową tożsamość wspólną dla wszystkich zwierząt.Swimmable.lua: Utwórz miksin z metodą:swim(), która wypisuje{self.name} is swimming. To zdolność — nie wszystkie stworzenia potrafią pływać, więc nie powinna być częścią łańcucha dziedziczenia.Bird.lua: Utwórz klasę Bird, która dziedziczy po Animal. Konstruktor:new(name, canSwim)powinien wywołać konstruktor klasy nadrzędnej i zapisać, czy ten ptak potrafi pływać. Zastosuj miksin Swimmable do klasy Bird. Dodaj metodę:chirp(), która wypisuje{name} chirps!. To zachowanie charakterystyczne dla ptaków.main.lua: Zdefiniuj tutaj funkcję pomocnicząapplyMixin. Odczytaj dane wejściowe: imię ptaka i informację, czy potrafi pływać (yeslubno). Utwórz instancję Bird, a następnie pokaż jej zdolności: wywołaj:eat()(odziedziczoną po Animal), wywołaj:chirp()(własną metodę Bird) i wywołaj:swim()tylko wtedy, gdy ptak potrafi pływać.
Otrzymasz dwa dane wejściowe:
- Imię ptaka (ciąg znaków)
- Informację, czy ptak potrafi pływać:
yeslubno
Jeśli ptak potrafi pływać, wynik powinien wyglądać tak:
{name} is eating
{name} chirps!
{name} is swimmingJeśli ptak nie potrafi pływać, wynik powinien wyglądać tak:
{name} is eating
{name} chirps!Na przykład, jeśli dane wejściowe to Duck i yes, wynik powinien wyglądać tak:
Duck is eating
Duck chirps!
Duck is swimmingJeśli dane wejściowe to Sparrow i no, wynik powinien wyglądać tak:
Sparrow is eating
Sparrow chirps!To wyzwanie pokazuje kluczową różnicę: Bird jest Animal (dziedziczenie), ale pływanie to opcjonalna zdolność (miksin). Miksin jest stosowany do klasy, ale logika w pliku main decyduje, czy go użyć, na podstawie zdolności danego ptaka!
Spróbuj swoich sił
-- main.lua: Punkt wejścia – utwórz ptaka i zademonstruj jego umiejętności
-- Funkcja pomocnicza do zastosowania miksinu w klasie
local function applyMixin(class, mixin)
-- TODO: Skopiuj wszystkie metody z miksinu do klasy
end
-- Zastosuj miksin Swimmable w klasie Bird przed załadowaniem modułu Bird
-- Uwaga: Może być konieczne zrobienie tego w Bird.lua lub tutaj
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Zastosuj miksin w klasie Bird
applyMixin(Bird, Swimmable)
-- Odczytaj dane wejściowe
local name = io.read()
local canSwimInput = io.read()
-- TODO: Ustal na podstawie danych wejściowych, czy ptak potrafi pływać ("yes" lub "no")
-- TODO: Utwórz instancję Bird z podanym imieniem i informacją o umiejętności pływania
-- TODO: Wywołaj eat() – metodę odziedziczoną z Animal
-- TODO: Wywołaj chirp() – metodę własną klasy Bird
-- TODO: Wywołaj swim() tylko wtedy, gdy ptak potrafi pływać
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