Stosowanie wielu mixinów
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 52 z 70.
Pojedyncza domieszka dodaje klasie jeden zestaw zachowań. Jednak rzeczywiste obiekty często potrzebują możliwości z wielu źródeł. Obiekt w grze może potrzebować możliwości poruszania się i bycia widocznym na ekranie: to dwie odrębne kwestie, które nie powinny znaleźć się w tej samej domieszce.
Rozwiązanie jest proste: zastosuj wiele domieszek do tej samej klasy. Każda domieszka wnosi własne metody, a klasa otrzymuje je wszystkie:
local Movable = {}
function Movable:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
end
local Visible = {}
function Visible:show()
print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
print("Hidden")
endZastosuj oba miksy do swojej klasy, używając tej samej funkcji pomocniczej co wcześniej:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local GameObject = {}
GameObject.__index = GameObject
applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)Teraz instancje GameObject mogą się poruszać i wyświetlać:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Wyświetlanie w 5, 3Jedna uwaga: jeśli dwie domieszki definiują metodę o tej samej nazwie, druga zastosowana nadpisze pierwszą. Dbaj o to, by metody w domieszkach miały różne nazwy, aby uniknąć nieoczekiwanego zachowania.
Wyzwanie
ŁatwyZbudujmy klasę Character do gry, która łączy wiele możliwości za pomocą mikserów! Twoja postać będzie mogła zarówno atakować wrogów, jak i leczyć samą siebie: to dwa odrębne zachowania pochodzące z osobnych mikserów.
Rozmieścisz swój kod w czterech plikach:
Attacker.lua: Utwórz mikser z metodą:attack(targetName), która wypisuje{self.name} attacks {targetName} for {self.damage} damage!(zakłada, że obiekt ma polanameidamage)Healer.lua: Utwórz mikser z metodą:heal(amount), która dodaje podaną wartość doself.healthi wypisuje{self.name} heals for {amount}. Health: {newHealth}Character.lua: Utwórz klasę Character z konstruktorem:new(name, health, damage), który zapisuje wszystkie trzy atrybuty. Zastosuj miksery Attacker i Healer, aby każda postać mogła atakować i leczyć się. Dodaj metodę:status(), która wypisuje{name} - Health: {health}, Damage: {damage}main.lua: Napisz tutaj funkcję pomocnicząapplyMixin. Wczytaj imię postaci, jej zdrowie i obrażenia, cel ataku oraz wartość leczenia. Utwórz postać Character, wyświetl jej status, każ jej zaatakować cel, uleczyć się, a następnie ponownie wyświetl jej status.
Otrzymasz pięć danych wejściowych:
- Imię postaci (ciąg znaków)
- Początkowy poziom zdrowia (liczba)
- Wartość obrażeń (liczba)
- Imię celu ataku (ciąg znaków)
- Wartość leczenia (liczba)
Wynik powinien pokazywać, jak postać korzysta z obu umiejętności mikserów:
{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}Na przykład, jeśli danymi wejściowymi są Warrior, 100, 25, Goblin i 30, wynikiem powinno być:
Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25Każdy mikser wnosi własną metodę do klasy Character. Postać nie dziedziczy po żadnym z mikserów. Po prostu zyskuje ich możliwości za pomocą funkcji applyMixin. Na tym polega siła łączenia wielu mikserów!
Spróbuj swoich sił
-- Plik główny
-- TODO: Napisz funkcję pomocniczą applyMixin
-- Ta funkcja powinna skopiować wszystkie metody z tabeli mixin do tabeli docelowej
local function applyMixin(target, mixin)
-- TODO: Zaimplementuj zastosowanie mixin
end
-- Udostępnij applyMixin globalnie, aby Character.lua mógł z niej korzystać
_G.applyMixin = applyMixin
local Character = require('Character')
-- Odczytaj dane wejściowe
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())
-- TODO: Utwórz obiekt Character o podanych wartościach name, health i damage
-- TODO: Wyświetl stan postaci
-- TODO: Spraw, by postać zaatakowała cel
-- TODO: Spraw, by postać się uleczyła
-- TODO: Ponownie wyświetl stan postaci
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