W Ruby, jak i w wielu innych
obiektowo-orientowanych językach programowania, dziedziczenie to mechanizm, dzięki któremu klasa może dziedziczyć właściwości i metody innej klasy. Klasa, która dziedziczy, nazywana jest podklasą, a klasa, od której się dziedziczy, - klasą nadrzędną.
Przykład złego dziedziczenia w Ruby
class Animal
def speak
puts "Animal speaks"
end
end
class Dog < Animal
def speak
puts "Dog barks"
end
end
class Cat < Animal
# Ta klasa nie definiuje swojej własnej metody speak
end
W tym przykładzie klasa
Dog dziedziczy klasę
Animal, ale klasa
Cat nie dostarcza własnej implementacji metody
speak. Jeśli spróbujesz stworzyć instancję klasy
Cat i wywołać metodę
speak, użyje ona implementacji z klasy
Animal. Może to być problemem, ponieważ dla kota bardziej odpowiednia jest odpowiedź -
meow.
Oczekujemy otrzymać tekst 'Cat meows', a otrzymujemy 'Animal speaks'. Wydaje się, że to nie jest bardzo krytyczne, ale w klasach odpowiedzialnych za logikę biznesową może to prowadzić do większych problemów.
Możemy zrobić następujące:
class Cat < Animal
def speak
puts "Cat meows"
end
end
To zdaje się rozwiązywać problem. Ale wyobraź sobie, że klas i metod jest dziesiątki lub setki. To podejście nie jest najlepszą praktyką.
Przykład dobrego dziedziczenia w Ruby
class Shape
def initialize(color)
@color = color
end
def draw
puts "Drawing a #{@color} shape"
end
end
class Circle < Shape
def initialize(color, radius)
super(color)
@radius = radius
end
def draw
puts "Drawing a #{@color} circle with radius #{@radius}"
end
end
class Square < Shape
def initialize(color, side_length)
super(color)
@side_length = side_length
end
def draw
puts "Drawing a #{@color} square with side length #{@side_length}"
end
end
W tym przykładzie jest klasa
Shape, która jest klasą bazową dla wszystkich figur geometrycznych. Klasa ma atrybut
color oraz metodę
draw, która wyświetla komunikat o rysowaniu.
Klasy Circle i Square dziedziczą klasę Shape i dostarczają własne atrybuty (radius i side_length) oraz implementację metody draw. Każda klasa może wywołać metodę draw, ale z własnym kontekstem i danymi.
To podejście pozwala na tworzenie wspólnych właściwości i metod w klasie bazowej, a następnie ich rozszerzanie w podklasach, zapewniając własną unikalną logikę. To sprawia, że kod jest bardziej czytelny, łatwiejszy w utrzymaniu i elastyczny.