Jeśli zaznajomiłeś/aś się już trochę z językiem Ruby, na pewno znasz podstawy programowania obiektowego. Pojęcia takie jak klasy, ich instacje (obiekty), dziedziczenie, czy moduły, nie są Ci obce. W tym krótkim artykule przedstawię działanie metody super
, która bywa bardzo przydatna, jeśli korzystamy z paradygmatów OOP.
Zadeklarujmy dwie klasy, z których druga dziedziczy z pierwszej:
class Dish
end
class Plate < Dish
end
Nie ma tu nic zaawansowanego. Klasa Plate
dzięki użyciu znaku <
dziedziczy (a więc zyskuje dostęp) do metod klasy Dish
. Jednakże na ten moment nie mamy żadnych metod. Zmieńmy to.
class Dish
def do_the_dishes
puts 'Washing, bulbulbulbul...'
end
def finish_meal
end
end
class Plate < Dish
end
W tym momencie możliwym jest wykonanie zarówno kodu:
dish = Dish.new
dish.finish_meal
Jak i:
plate = Plate.new
plate.finish_meal
Właśnie dzięki dziedziczeniu metod klasy Dish
przez klasę Plate
. Przejdźmy zatem do metody super
i tego w czym może być nam pomocna.
Załóżmy, że na przyjęciu mamy osoby mięsożerne oraz wegetarian. Jeśli w potrawie jest mięso, wegetarianin za nią podziękuje, natomiast wszystkożerca skończy posiłek, zjadając go. “Nadpiszemy” teraz metodę finish_meal
w klasie Plate, tak, aby przed zakończeniem posiłku sprawdzać, czy na talerzu znajduje się mięso. Jeśli nie - gość śmiało może go ‘sfiniszować’. Jeśli jest - podziękuje.
class Dish
def finish_meal
puts 'So good, thank you!'
end
end
class Plate < Dish
def finish_meal(meal_kind)
puts meal_kind == 'meat' ? 'No, thank you.' : 'So good, thank you!'
end
end
Powyższy kod działa, wegetarianie i mięsożercy są zadowoleni. Ale gdzie ta metoda super? Aby zachować zasadę DRY, możemy wykorzystać super
do pozbycia się zduplikowanego kodu "So good, thank you!"
w klasie Plate
.
class Dish
def finish_meal
puts 'So good, thank you!'
end
end
class Plate < Dish
def finish_meal(meal_kind)
puts meal_kind == 'meat' ? 'No, thank you.' : super()
end
end
plate = Plate.new
plate.finish_meal('meat') # 'No, thank you.'
plate.finish_meal('soy') # 'So good!'
Kiedy ‘mięsny’ warunek zostanie spełniony (pierwszy przypadek), otrzymamy odpowiedź “No, thank you”. W odwrotnym przypadku obiekt plate
dzięki metodzie super
, potocznie mówiąc, wejdzie poziom wyżej w poszukiwaniu metody o tej samej nazwie w klasie z której dziedziczy. Znajdzie ją w klasie Dish
i tam ją wykona. Warto zwrócić uwagę na sposób w jaki wywołujemy metodę super()
. Musimy dodać puste nawiasy, aby metoda wyżej została wykonana bez przekazywania argumentów z metody Plate#finish_meal
. Jest to spowodowane tym, że Dish#finish_meal
nie wymaga (a nawet po prostu nie przyjmuje) żadnych argumentów, natomiast Plate#finish_meal
musi mieć jeden. Jeśli napisalibyśmy po prostu super
, wykonując kod z argumentem innym niż ‘meat’ otrzymalibyśmy ArgumentError
(dlatego, że instrukcja warunkowa skieruje nas do wykonania super
).
- jeżeli wywołamy
super
bez żadnych argumentów, spowoduje to przekazanie wszystkich podanych argumentów do metody klasy poziom ‘wyżej’, - jeżeli wywołamy
super()
z pustymi nawiasami na końcu, spowodujemy, że żadne argumenty nie zostaną przekazane (nawet jeśli w aktualnej metodzie jakieś były - przedstawiony przypadek), - jeżeli wywołamy
super(one, two)
przekażemy dokładnie argumenty “one” oraz “two”, nawet jeśli do aktualnej metody przekazane zostały [“one”, “two”, “three”, “hundred”]