Relacja Has-A
Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 49 z 70.
W trakcie tego kursu używałeś dziedziczenia, aby współdzielić zachowanie między klasami: pies „jest” zwierzęciem. Jednak dziedziczenie nie zawsze jest odpowiednim narzędziem.
Czasami obiekty nie dzielą wspólnego drzewa rodzinnego; zamiast tego jeden obiekt po prostu zawiera inny. Nazywa się to kompozycją i modeluje relację „ma”.
Weźmy samochód. Samochód nie jest rodzajem silnika. Ma silnik. Silnik to osobny obiekt, który znajduje się w samochodzie:
local Engine = {}
Engine.__index = Engine
function Engine:new(horsepower)
local obj = {horsepower = horsepower}
setmetatable(obj, Engine)
return obj
end
local Car = {}
Car.__index = Car
function Car:new(model, engine)
local obj = {
model = model,
engine = engine -- Przechowuj obiekt Engine wewnątrz Car
}
setmetatable(obj, Car)
return obj
endPodczas tworzenia samochodu przekazujesz istniejący obiekt silnika do jego konstruktora:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200Samochód nie dziedziczy po silniku. Po prostu przechowuje do niego referencję. Dzięki temu obie klasy pozostają niezależne. Możesz podmienić silnik na inny albo użyć tej samej klasy silnika w motocyklu.
Kompozycja daje ci elastyczność, której nie zapewnia dziedziczenie: składasz obiekty z części, zamiast zamykać je w sztywnej hierarchii rodzinnej.
Wyzwanie
ŁatwyZbudujmy system komputerowy z użyciem kompozycji! Zamiast dziedziczenia utworzysz Computer, który ma w sobie CPU: demonstrując relację „ma”.
Rozmieścisz kod w trzech plikach:
CPU.lua: Utwórz klasę CPU z konstruktorem:new(brand, speed), który przechowuje nazwę marki i szybkość w GHz. Dodaj metodę:getInfo(), która zwraca ciąg znaków w formacie:{brand} @ {speed}GHzComputer.lua: Utwórz klasę Computer, której konstruktor:new(model, cpu)przyjmuje nazwę modelu i obiekt CPU. Computer przechowuje obiekt CPU wewnątrz siebie (kompozycja!). Dodaj metodę:describe(), która wyświetla model komputera i informacje o jego CPU.main.lua: Połącz wszystko w całość! Wczytaj dane wejściowe dotyczące marki CPU, szybkości CPU i modelu komputera. Najpierw utwórz obiekt CPU, a następnie przekaż go do konstruktora Computer. Na koniec wywołaj metodę:describe()komputera.
Otrzymasz trzy dane wejściowe:
- Marka CPU (ciąg znaków)
- Szybkość CPU w GHz (liczba)
- Nazwa modelu komputera (ciąg znaków)
Wynik powinien przedstawiać opis komputera, który zawiera informacje o zawartym w nim CPU:
Model: {model}
CPU: {brand} @ {speed}GHzNa przykład, jeśli danymi wejściowymi są Intel, 3.5 i WorkStation, wynikiem powinno być:
Model: WorkStation
CPU: Intel @ 3.5GHzZwróć uwagę, że Computer nie dziedziczy po CPU. Po prostu przechowuje do niego referencję. Metoda :describe() klasy Computer uzyskuje dostęp do danych CPU za pośrednictwem przechowywanego obiektu, pokazując, jak kompozycja pozwala budować złożone obiekty z prostszych części!
Spróbuj swoich sił
-- main.lua
-- Połącz wszystko za pomocą kompozycji
local CPU = require('CPU')
local Computer = require('Computer')
-- Odczytaj dane wejściowe
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()
-- TODO: Utwórz obiekt CPU z podaną marką i szybkością
-- TODO: Utwórz obiekt Computer z podanym modelem i obiektem CPU
-- TODO: Wywołaj metodę describe() obiektu Computer, aby wyświetlić wynik
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