Delegowanie
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 50 z 70.
Przechowywanie obiektu wewnątrz innego obiektu to dopiero pierwszy krok. Prawdziwa siła kompozycji wynika z delegowania, gdy zewnętrzny obiekt przekazuje wywołania metod swoim wewnętrznym komponentom.
Pomyśl o uruchamianiu samochodu. Nie sięgasz pod maskę, żeby ręcznie obracać silnikiem. Zamiast tego przekręcasz kluczyk, a samochód zajmuje się resztą wewnętrznie.
W kodzie oznacza to, że klasa Car udostępnia prostą metodę :start(), która deleguje właściwą pracę do silnika:
function Car:start()
print("Turning the key...")
self.engine:run() -- Deleguj do obiektu wewnętrznego
endSamochód nie musi wiedzieć, jak działa silnik. Po prostu mówi silnikowi, żeby wykonywał swoją pracę. Dzięki temu obowiązki są rozdzielone. Klasa Engine obsługuje logikę silnika, a Car koordynuje poszczególne elementy:
function Engine:run()
print("Engine running at " .. self.horsepower .. " HP")
end
local engine = Engine:new(250)
local car = Car:new("Coupe", engine)
car:start()
-- Wynik:
-- Przekręcanie kluczyka...
-- Silnik pracuje z mocą 250 HPDelegowanie tworzy przejrzysty interfejs dla użytkowników Twojej klasy. Wchodzą w interakcję z samochodem, a nie z jego wewnętrznymi częściami. Jeśli później zamienisz silnik na inną implementację, publiczny interfejs samochodu pozostanie taki sam — zmieni się tylko wewnętrzne delegowanie.
Wyzwanie
ŁatwyZbudujmy system odtwarzacza muzyki, który pokazuje delegowanie! Utworzysz komponent Speaker oraz MusicPlayer, który go zawiera. Odtwarzacz nie będzie bezpośrednio obsługiwać odtwarzania dźwięku, lecz przekaże tę odpowiedzialność swojemu wewnętrznemu głośnikowi.
Rozmieścisz swój kod w trzech plikach:
Speaker.lua: Utwórz klasę Speaker z konstruktorem:new(volume), który przechowuje poziom głośności. Dodaj metodę:playSound(trackName), która wyświetla:Playing "{trackName}" at volume {volume}MusicPlayer.lua: Utwórz klasę MusicPlayer, której konstruktor:new(brand, speaker)przyjmuje nazwę marki i obiekt Speaker. Odtwarzacz przechowuje w sobie głośnik. Dodaj następujące metody::play(trackName): wyświetla{brand} player starting..., a następnie przekazuje zadanie metodzie:playSound()głośnika:stop(): wyświetla{brand} player stopped
main.lua: Połącz wszystko! Wczytaj dane wejściowe: głośność głośnika, markę odtwarzacza i nazwę utworu. Najpierw utwórz Speaker, a następnie przekaż go do konstruktora MusicPlayer. Wywołaj metodę:play()odtwarzacza, podając nazwę utworu, a następnie wywołaj:stop().
Otrzymasz trzy dane wejściowe:
- Głośność głośnika (liczba)
- Marka odtwarzacza muzyki (ciąg znaków)
- Nazwa utworu do odtworzenia (ciąg znaków)
Wynik powinien pokazywać, jak odtwarzacz współpracuje ze swoim głośnikiem:
{brand} player starting...
Playing "{trackName}" at volume {volume}
{brand} player stoppedNa przykład, jeśli dane wejściowe to 75, SoundMax i Summer Vibes, wynik powinien wyglądać tak:
SoundMax player starting...
Playing "Summer Vibes" at volume 75
SoundMax player stoppedZwróć uwagę, że MusicPlayer nie wie, jak wytwarzać dźwięk. Po prostu poleca swojemu głośnikowi zająć się tą częścią. Tak właśnie działa delegowanie: obiekt zewnętrzny udostępnia przejrzysty interfejs, a komponent wewnętrzny wykonuje specjalistyczną pracę!
Spróbuj swoich sił
-- main.lua
-- Połącz wszystko w całość!
local Speaker = require('Speaker')
local MusicPlayer = require('MusicPlayer')
-- Odczytaj dane wejściowe
local volume = tonumber(io.read())
local brand = io.read()
local trackName = io.read()
-- TODO: Utwórz Speaker z podaną głośnością
-- TODO: Utwórz MusicPlayer z podaną marką i obiektem Speaker
-- TODO: Wywołaj metodę play obiektu MusicPlayer, przekazując nazwę utworu
-- TODO: Wywołaj metodę stop obiektu MusicPlayer
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