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”]