Dziedziczenie na przykładzie języka Ruby

Opublikował siefca n 29 mar 2009 14:32:10 GMT

W niedawno opublikowanym artykule pisałem o klasach i obiektach w Rubym. Jednak nie wyjaśniłem tam pojęcia dziedziczenie, które jest jednym z podstawowych mechanizmów wykorzystywanych w programowaniu obiektowym. Kiedy przydaje się dziedziczenie? Żeby sobie na to pytanie odpowiedzieć, przypomnij sobie do czego służą klasy. Są to w skrócie nowe typy danych, które sam tworzysz. Możesz w nich określić jakie dane będą przechowywały obiekty tworzonych przez Ciebie klas, a także jakie operacje na tych danych będzie można szybko i sprawnie wykonać.

Wyobraź sobie, że masz już stworzony nowy typ danych określony klasą Osoba:

  class Osoba

    attr_accessor :wiek, :imie, :plec

    def pokaz
      puts "#{@imie} ma #{@wiek} lat i jest #{@plec ? 'kobieta' : 'mezczyzna' }"
    end

  end

Zauważysz, że tworzonych jest sześć akcesorów (trzy do zapisu, a trzy do odczytu), czyli metod, których jedyną funkcją jest ustawiać bądź odczytywać zawartość zmiennych instancyjnych o takich jak one nazwach. Naprawdę klasa ta wygląda więc tak:

  class Osoba
    def wiek; @wiek end
    def wiek=(x); @wiek = x end
    def imie; @imie end
    def imie=(x); @imie = x end
    def plec; @plec end
    def plec=(x); @plec = x end

    def pokaz
      puts "#{@imie} ma #{@wiek} lat i jest #{@plec ? 'kobieta' : 'mezczyzna' }"
    end
  end

Wyobraź sobie teraz, że przychodzi ktoś do Ciebie i prosi o rozszerzenie programu o możliwość operowania w nim na informacjach o studentach. Chodzi o najpotrzebniejsze informacje: imię, wiek, płeć i kierunek studiów. Możesz wtedy stworzyć kolejną klasę, która wyglądała będzie na przykład tak:

  class Student

    attr_accessor :wiek, :imie, :plec, :kierunek

    def pokaz
      puts "#{@imie} ma #{@wiek} lat i jest #{@plec ? 'kobieta' : 'mezczyzna' }"
      puts "studiuje na kierunku: #{@kierunek}"
    end

  end

Takie podejście ma jednak niewielką wadę: powtarzasz się. Masz już w programie klasę Osoba, a stwarzasz bardzo podobną klasę Student. Dlaczego ta druga klasa jest podobna? Bo student jest osobą, a reprezentujący go typ danych pochodzi od typu reprezentującego osobę. Dlatego większość języków obiektowych obsługuje coś co nazywa się dziedziczeniem. Polega ono na tym, że tworzysz klasę pochodną względem już istniejącej. W naszym przypadku tak:

  class Osoba

    attr_accessor :wiek, :imie, :plec

    def pokaz
      puts "#{@imie} ma #{@wiek} lat i jest #{@plec ? 'kobieta' : 'mezczyzna' }"
    end

  end

  class Student < Osoba

    attr_accessor :kierunek

    def pokaz
      super
      puts "studiuje na kierunku: #{@kierunek}"
    end

  end

  s = Student.new
  s.imie = "Jan Kowalski"
  s.plec = false
  s.wiek = 25
  s.kierunek = "Informatyka"

  s.pokaz

  # => Jan Kowalski ma 25 lat i jest mezczyzna
  # => studiuje na kierunku: Informatyka 

Na czym polega dziedziczenie?

Dziedziczenie polega na tym, że nowa klasa może odwoływać się do metod zdefiniowanych w klasie po której ona dziedziczy. Jest to po prostu rozszerzanie funkcjonalności już istniejącego typu danych o jakieś nowe elementy. W praktyce interpreter języka pamięta w każdej klasie odpowiedni wskaźnik kierujący do klasy nadrzędnej (superklasy). Jeśli wywołasz metodę, która nie została zdefiniowana, to interpreter korzysta z tego odnośnika i zagląda do nadklasy, czy aby tam nie ma tej metody. Czynność ta jest powtarzana, aż do momentu, w którym ścieżka dziedziczenia się zakończy.

W języku Ruby nie istnieje wielokrotne dziedziczenie, a więc klasy mogą mieć tylko jednego bezpośredniego przodka, nazywanego też rodzicem lub klasą bazową. W powyższym przypadku klasa Student jest klasą pochodną, a klasa Osoba jest jej klasą bazową. Istnieją sposoby, aby metoda klasy dowiedziała się po czym jej obiekt dziedziczy. Co więcej, instancje klasy, czyli obiekty są już w nie wyposażone:

  s.class  # zwraca obiekt reprezentujący klasę obiektu
  s.class.superclass # zwraca obiekt reprezentujący klasę bazową obiektu

Klasy Object i Class

Jak to? Przecież nie tworzyliśmy metody o nazwie class ani klasowej metody superclass, więc skąd się one wzięły? Żeby ułatwić życie programiście w języku Ruby wszystkie tworzone klasy dziedziczą po klasie Object. Wyjątkiem są oczywiście te klasy, które dziedziczą po innych klasach, jak w przypadku klasy Student. Jednak klasa Osoba będąca przodkiem klasy Student jest potomkiem klasy Object, a więc ścieżka dziedziczenia zaprowadzi nas w końcu do zdefiniowanej metody class.

Właśnie w klasie Object znajdziesz metodę class, która zwróci obiekt klasy, dla której została ona wywołana. Brzmi to dziwacznie, ale w Rubym nawet klasy są specyficznymi obiektami.

Z kolei metoda superclass zdefiniowana jest w klasie Class, która jest klasą wszystkich obiektów będących klasami. Zauważ, że w podobny sposób dostępna jest między innymi metoda klasowa new, używana do tworzenia nowych obiektów. Aby przekonać się, że klasy są też obiektami (instancjami klasy Class) możesz użyć konstrukcji:

  Student.class

Metody klasy nadrzędnej

W kodzie definiującym klasę Student znajdziesz metodę pokaz, której nazwa jest taka sama, jak nazwa metody z klasy bazowej Osoba. Tak więc interpreter wywoła właśnie ją i nie będzie już zawracał sobie głowy poszukiwaniami w superklasie. Jednak nasza metoda ma uzupełniać, a nie przesłaniać działanie oryginalnej i dlatego korzystamy w niej ze słowa kluczowego super. Umieszczenie go w dowolnej metodzie sprawi, że zostanie wywołana ta sama metoda zdefiniowana w superklasie, pod warunkiem, że w klasie bazowej taka metoda istnieje.

Podziel się

Trackbacki

Użyj następującego trackbacka na swojej stronie:

http://randomseed.pl/trackbacks?article_id=dziedziczenie-w-ruby&day=17&month=04&year=2009

Komentarze

  1. Oyun powiedział 6 months later:

    thanks a lot.. it’s very good article.

    Ten komentarz oczekuje na akceptację. Nie ukaże się do czasu zaakceptowania przez autora..

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz