Ruby on Rails I: z czym to się je?

Opublikował siefca wt 23 gru 2008 02:38:35 GMT

Ruby on Rails Dawno, dawno temu usłyszałem o środowisku służącym do tworzenia aplikacji webowych o nazwie Ruby on Rails (RoR), a przed kilkoma miesiącami, powodowany radością życia i zaciekawieniem, zdobyłem się na próbę zainstalowania railsowej aplikacji – silnika napędzającego randomseed. Wcześniej uczyłem się języka Ruby i klikałem w nim jakieś proste programy w celach testowych. Teraz przyszedł taki moment, że w najbliższych tygodniach zamierzam zrobić kompletnie od podstaw aplikację WWW napędzaną przez RoR. W tym celu, żeby przypomnieć sobie niektóre rzeczy, ustrukturalizować to co już wiem i nauczyć się tego co mi umknęło, zamierzam o tym pisać. Zacznę od początku, czyli od tego co to jest Ruby i co to są Rails.

Ruby

Ruby to interpretowany, dynamiczny, obiektowy język programowania stworzony w latach dziewięćdziesiątych przez programistę pochodzącego z Kraju Kwitnącej Wiśni. (Właśnie dodaję definicję języka dynamicznego do Wikipedii, gdzie można sobie poczytać czym się to różni i jak ma się do np. dynamicznego typowania, które w Rubym też jest obsługiwane).

Składnia i zachowanie przypominają trochę Pythona, chociaż Ruby jest troszeczkę bardziej obiektowy. W Rubym nawet liczby całkowite są instancjami klasy Fixnum i pełnoprawnymi obiektami. Poza tym instancja jakiejś klasy również zachowuje się trochę jak samodzielna klasa. Co to jest instancja i co to jest klasa będzie wyjaśnione za momencik. Pamiętasz operator wyboru składowej z C++ i innych języków, zwany też operatorem kropki? W Rubym, dzięki wszechobecnej obiektowości, można traktować go wręcz jako łącznik komunikatów, przez którego obiekty będące rezultatami poprzednio wykonanych operacji wędrują od jednej metody do drugiej.

Ruby jest nieco bardziej śmietniskowy niż Python, ale znacznie mniej niż dominujący w tej kategorii Perl. Pisze się w nim przyjemnie i nie trzeba co rusz, jak to bywa w Pythonie, nadużywać słowa kluczowego self. Zauważyłem też, że ludzie kodujący w tym języku szanują swoje dobre samopoczucie, wygodę programowania i elegancję kodu. Wydało mi się to ciekawe i inspirujące. Nie zamierzam pisać więcej o samym języku, ale mam niespodziankę w postaci konwencji: jeśli będę przedstawiał jakiś kod związany bezpośrednio lub nie ze środowiskiem Rails i pojawią tam nie tłumaczone jeszcze konstrukcje, to postaram się przy okazji wytłumaczyć syntaktykę Ruby’ego, ok? Dzięki temu unikniemy formalnego etapu polegającego na wcześniejszej nauce języka w oderwaniu od praktycznego zastosowania.

Ruby on Rails

Ruby on Rails to środowisko, w którym według zapewnień twórców i wielu użytkowników, szybko i przyjemnie stworzysz aplikację WWW. Można je traktować jako zbiór generatorów szkieletów, które służą do budowania swoich własnych aplikacji.

MVC

Podział zadań to dobra rzecz. Odkryli to nawet pospolici przestępcy i w celu zwiększenia skuteczności stosują pewne wzorce projektowe. Na przykład chłopaki kradnący samochody dzielą się w tym celu na grupy: jedni chodzą na robotę, inni zajmują się szeroko pojętym tunningiem, a jeszcze inni upłynniają towar. Dzięki temu szef wszystkich szefów może zarządzać strukturą, a zasoby ludzkie są grupowane w zależności od dominujących u jednostek kompetencji. Mamy tu do czynienia z podziałem zadań na etapie planowania, który przekłada się potem na podział kompetencji.

W RoR wykorzystywany jest wzorzec projektowy MVC (Model-Widok-Kontroler) ułatwiający taki właśnie podział obowiązków w trakcie budowania oprogramowania i w trakcie jego przetwarzania w komputerowym systemie. W skrócie chodzi o wyróżnienie w projekcie trzech podstawowych części: związaną z obsługą danych z bazy (Model), związaną z porządkowaniem informacji i odpowiedzialną za przygotowywanie ich do wyświetlenia (Kontroler) oraz generującą dane które trafią do klienta (Widok).

Nie musisz od razu rozumieć wszystkich pojęć użytych poniżej w celu doprecyzowania implementacji MVC w Rails – w dalszej części tego dokumentu znajdziesz praktyczne wyjaśnienia wykorzystanych terminów związanych ze składnią języka Ruby.

Model

Pisząc aplikację w RoR większość jej podstawowej logiki związanej ze strukturami danych umieszczamy w pochodnych klasy ActiveRecord::Base odpowiedzialnej za obsługę Modelu, czyli warstwy reprezentującej dane znajdujące się w bazie. Jednak w Rails Model to nie tylko strukturalizacja, ale również operacje na danych, na przykład ich konwertowanie czy weryfikacja. Ważne jest też sprawdzanie zależności między poszczególnymi informacjami i inne czynności, które zazwyczaj umieszcza się w procedurach wbudowanych bazy danych wspomagając się kluczami obcymi (ang. foreign keys). Jeśli chcesz używać Rails, to zaleca się, aby tego typu mechanizmy umieszczać w Modelu, a nie w bazie o konkretnym typie. Dzięki temu całość będzie bardziej przenośna i elastyczna.

Na załączonym poniżej rysunku zauważysz, że obok pochodnej klasy odpowiedzialnej za Modele umieściłem opis „logika”. Może to budzić pewien sprzeciw, ponieważ w modelu MVC kontrolą procesu biznesowego zajmuje się komponent, o którym za chwilę, natomiast ActiveRecord jest umownie mówiąc klasą służącą do obiektowej reprezentacji relacyjnych baz danych. Jednak takie podejście byłoby ograniczaniem możliwości i siły tkwiącej w Modelu z Rails. Przez logikę w odniesieniu do tego typu komponentów (pochodnych wspomnianej klasy) rozumiemy mechanizmy biznesowej organizacji struktur danych, nie zaś organizacji procesu ich przetwarzania.

Widok

Jeśli chodzi o znane z różnych systemów zarządzania zawartością wzorce i szablony tworzenia dokumentów wyjściowych, to ich odpowiednikiem w Rails są klasy i metody zaliczane do Widoku. W praktyce wygląda to tak, że możemy po prostu w odpowiednich katalogach tworzyć pliki o pewnych nazwach, które zostaną wczytane i przetworzone z użyciem metod obecnych w instancji klasy ActionView::Base. Wygenerowany kod może być na przykład sformatowany zgodnie ze schematem XML albo HTML. W przypadku tej drugiej opcji użyty będzie zapewne system szablonowy ERB. Dzięki niemu można włączać fragmenty napisane w Rubym bezpośrednio do kodu HTML (pliki .rhtml).

Gdyby zaszła konieczność filtrowania danych tuż przed ich renderowaniem, czy odwołania się do jakichś wymyślnych metod generowania właściwego wyglądu, to zamiast zaśmiecać pliki z wzorcami możemy potrzebne fragmenty zdefiniować jako tzw. helpers. Te pomocnicze metody można potem wywoływać z poziomu wzorca dokumentu. Oczywiście same metody renderowania również da się modyfikować rozszerzając ActionView::Base lub przesłaniając jej metody.

Kontroler

Rzeczy związane z obsługą żądań przeglądarki i tworzeniem schematu przepływu (logika procesu biznesowego) dla czynności użytkownika definiujemy jako pochodne klasy ActionController::Base. Kontroler jest komponentem, który z jednej strony korzysta z Modelu, a z drugiej używa Widoku i przedstawia odpowiednio przetworzone dane. To właśnie w tym miejscu programista decyduje, w jaki sposób zachowa się aplikacja i jak jej działanie odzwierciedli założenia projektu.

Poniższy szkic daje ogólne pojęcie o tym jak opracowano Model-Widok-Kontroler w Ruby on Rails:

Schemat MVC w Ruby on Rails

Klasy, metody i pochodne

Jednak Ruby on Rails to nie tylko te trzy przedstawione na schemacie klasy. Za chwilę postaram się omówić wszystkie ważniejsze, choć wcześniej – zgodnie z obietnicą – wyjaśnię, co mamy na myśli mówiąc klasa, obiekt, metoda i pochodna. Otóż w obiektowych językach programowania mamy przede wszystkim do czynienia z definiowaniem własnych typów zmiennych i ich zachowań.

Klasy i obiekty

Klasa to po prostu nowy typ danych stworzony przez programistę, a obiekt to jeden egzemplarz danych tego typu. W języku Ruby nawet liczby całkowite są takimi pełnoprawnymi obiektami. Jeśli potrzebujesz dowiedzieć się więcej o klasach, obiektach, metodach i polach to zapraszam do przeczytania wpisu “Co to są klasy i obiekty”.

Oto przykład klasy i obiektu:

# utworzenie klasy (nowego typu danych)
class Klasa
  # definicja metody (funkcji składowej tej klasy)
  def metoda
     puts "test"
  end
end

# utworzenie obiektu na podstawie klasy
obiekt = Klasa.new

# wywołanie metody obiektu
obiekt.metoda

Zróbmy więc sobie klasę, która podeprze podany wcześniej przykład:

# Definiujemy klase Pieniadz.
class Pieniadz
  public
  attr_accessor :wieksza
  attr_accessor :mniejsza
  @wieksza   # calosci
  @mniejsza  # setne
end

Akcesory

Zauważ, że w kodzie pojawiły się dziwne znaczki i słówko public, a także dwa razy attr_accessor. A więc: małpka przed nazwą zmiennej oznacza, że będzie to nasza zmienna instancji klasy, czyli struktura danych, dla której pamięć jest rezerwowana przy każdym tworzeniu obiektu. Słówko attr_accessor to nic innego jak automatyczne utworzenie metod o nazwach takich samych jak użyte zmienne instancji. Po co? Ano po to, żeby można było z zewnątrz odwoływać się do naszych zmiennych. W języku Ruby nie „widać” pól klasy i nie można do nich pisać, ani z nich czytać, chyba że stworzy się metody o takich samych jak pola nazwach. Jeśli chcielibyśmy dać wyłącznie możliwość odczytu, to możemy też użyć “makra” attr_reader, a tylko możliwość zapisu gwarantuje nam attr_writter. Dwukropek przed nazwą pola (czyli zmiennej klasy) w momencie definiowania akcesora jest po to, aby interpreter otrzymał łańcuch internalizowany, czyli symbol. Działa to szybciej niż łańcuch znaków i jest wymagane konwencją – jeśli chcesz wiedzieć więcej o internalizacji zerknij na Wikipedię, gdzie pokusiłem się o dodanie wpisu, bo sam nie wiedziałem jak to działa.

Jeśli nie chcemy korzystać z omawianych automatycznych uproszczeń, to zawsze możemy samodzielnie napisać równoważny kod:

# Definiujemy klase Pieniadz.
class Pieniadz
  public
  @wieksza   # calosci
  @mniejsza  # setne

  # metody do odczytywania
  def mniejsza; @mniejsza; end 
  def wieksza; @wieksza; end

  # metody do zapisywania
  def mniejsza=(value); @mniejsza=value; end
  def wieksza=(value); @wieksza=value; end
end

Zauważ, że w metodach zwracających zawartość zmiennych nie ma znanego z wielu języków słowa return. Nie jest ono potrzebne, ponieważ funkcje w Rubym zwracają wynik ostatnio obliczanego wyrażenia. Oczywiście można zwrócić wartość wprost, ale nie korzysta się z tego nazbyt często. Przy okazji, słowo kluczowe public informuje interpreter, że wszystkie metody i pola poniżej są składowymi publicznymi klasy. Oznacza to, że zebrane w tej sekcji metody można wywoływać, a pola można odczytywać i zapisywać z zewnątrz. Możesz pominąć to słowo kluczowe, jeśli chcesz, ponieważ jest ono domyślnie przyjmowane, chyba że pojawią się inne konstrukcje, o których wspomnę potem.

Uwzględnianie typów obiektów

A teraz wersja tej klasy wzbogacona o metodę służącą do dodawania i metodę konwersji do liczby zmiennoprzecinkowej:

# Definiujemy klase Pieniadz.
class Pieniadz
  public
  attr_accessor :wieksza
  attr_accessor :mniejsza
  @wieksza   # calosci
  @mniejsza  # setne

  # konstruktor
  def initialize(w=0,m=0)
    @wieksza = 0
    @mniejsza = 0
    if (m != 0)
      @wieksza = w.to_i;
      @mniejsza = m.to_i;
    else
      self.dodaj(w)
    end
  end

  # metoda dodawania liczby do pieniadza
  def dodaj(p)
    if p.is_a?(Float)
      @wieksza += p.to_i
      @mniejsza += ((p - @wieksza) * 100).to_i
    elsif p.is_a?(Pieniadz)
      @wieksza += p.wieksza
      @mniejsza += p.mniejsza 
    else
      @wieksza += p.to_i
    end
  end

  # metoda konwersji do liczby zmiennoprzecinkowej
  def to_f
      @wieksza.to_f + @mniejsza.to_f / 100
  end
end

# testujemy

a = Pieniadz.new(2,50)
b = Pieniadz.new(a)
puts b.to_f

Widzimy tutaj parę nowych rzeczy. Na przykład metoda is_a? należąca do przyjmowanego jako argument metody dodaj obiektu p. Skąd wiemy, że przekazany obiekt będzie miał taką metodę? Można być tego pewnym, ponieważ każdy obiekt języka Ruby dziedziczy po specjalnej klasie Object. Właśnie tam mamy tą metodę – zwraca ona logiczną wartość (prawda lub fałsz) w zależności od tego, czy obiekt, którego dotyczy jest instancją podanej w parametrze klasy lub instancją klasy, po której podany w parametrze obiekt dziedziczy. O dziedziczeniu za momencik – wtedy poznasz, dlaczego Twoja klasa może mieć metody, których samodzielnie nie definiujesz. Używam wspomnianej przed momentem konstrukcji, żeby zbadać, czy podany argument jest obiektem typu Float. Jeśli tak jest, to znaczy, że wprowadzony obiekt jest liczbą zmiennoprzecinkową, więc należy ją rozbić na część całkowitą (metoda to_i) i resztę; część całkowitą dodajemy do pola wieksza a resztę do pola mniejsza. Kolejne wyrażenie warunkowe sprawdza, czy czasami podany obiekt nie jest typu Pieniadz i jeśli tak jest, to po prostu kopiujemy odpowiednie pola. Na końcu wyrażenia warunkowego przyjmujemy, że w pozostałych przypadkach należy dokonać konwersji podanego argumentu do liczby całkowitej (to_i) i dodać tak uzyskaną wartość do pola wieksza.

Konstruktor

Metoda dodaj() poza tym, że możliwa do wykorzystania w programie, jest wywoływana wewnątrz innej metody initialize(). Jest to konstruktor klasy, czyli specjalna metoda, która jest wywoływana automatycznie, gdy tylko tworzona jest instancja klasy, czyli nowy obiekt. Często programista musi w jakiś sposób zainicjować pola klasy, nadając im wartości początkowe, czy też dokonać innych operacji, które upewnią go, że tworzony obiekt będzie poprawny i przygotowany do pracy. Wtedy definiuje się metodę konstruktora. W powyższym przykładzie może przyjmować ona dwa argumenty, ale określono ich wartości domyślne, więc przy wywoływaniu można pominąć jeden z nich lub oba.

Konwersja typów

W krótkiej metodzie na końcu (o nazwie to_f) wyposażamy klasę w możliwość konwersji typów. Konwersja typów może mieć miejsce na przykład wtedy, gdy zechcemy wykonywać operacje na dwóch obiektach należących do różnych klas. Zauważ, że interpreter może nie mieć pojęcia co zrobić, gdy spróbujesz dodawać jakąś liczbę do obiektu typu Pieniadz. Zanim więc dodasz do obiektu klasy Pieniadz jakąś liczbę musisz sprawdzić co to za liczba (może całkowita, a może taka „z przecinkiem”?) i wartość jakiego pola (a może obu?) ona zwiększy. Konwersję można zrealizować definiując funkcję, ale znacznie lepszym wyjściem jest postępować zgodnie z konwencją i napisać ją jako metodę klasy, której dotyczy.

Istnieje konwersja jawna i niejawna Ta pierwsza polega na wywołaniu odpowiedniej metody zanim dokonamy operacji. Przykładem może być zamiana liczby całkowitej na jej znakową reprezentację przed próbą „dodania” jej do obiektu przechowującego napis tekstowy. Można to zrobić tak:

a = 5
b = " jakiś tekst"
puts a.to_s + b  # konwersja liczby do łańcucha tekstowego i wyświetlenie łańcuchów

Konwersja niejawna ma miejsce wtedy, gdy jakaś konstrukcja języka wywołuje metodę konwertującą automatycznie, na przykład:

class Pisak
  def to_s
    "robią mi konwersję"
  end
end

a = Pisak.new
puts a

Uruchamiając ten kod odkryjemy, że próba wyświetlenia obiektu tak naprawdę będzie oznaczała wywołania metody to_s. W Ruby zastosowano elegancki sposób konwertowania, który nie wymaga używania jakiejś specjalnej konstrukcji językowej i nie wprowadza operatorów konwersji, które trzeba przeciążać, aby tworzyć własne mechanizmy rzutowania typów. Nazywa się to kaczym typowaniem (ang. duck typing) i chodzi w tym o to, że konwersja z jednego typu do drugiego jest tak naprawdę wykonywana bez angażowania interpretera w analizowanie typów. Jeżeli jakiś obiekt posiada metodę konwersji do innego typu, to przyjmuje się, że konwersja jest możliwa. Przy niejawnym rzutowaniu jest poszukiwana odpowiednia, zgodna z konwencją nazwa metody (np. to_s w przypadku konieczności konwersji do typu String) i jeśli ona istnieje, to jest wywoływana. Nazwa „kacze typowanie” pochodzi od porzekadła „jeśli kwacze jak kaczka i skacze jak kaczka, to musi być kaczka”. Duck typing jest mechanizmem znajdującym zastosowanie nie tylko przy konwersji typów, ale wszędzie tam, gdzie jakaś klasa ma możliwość operowania za pomocą metody na danych pewnego typu. Właśnie nazwa metody jest tu kluczem, zwróć uwagę, jak typ w gruncie rzeczy nie ma znaczenia, dopóki istnieją metody, które powinny obsługiwać związane z typem operacje:

# klasa Kaczka
class Kaczka
  def kwacz
    puts "kwa kwa kwa"
  end
end

# klasa Kura udająca kaczkę
class Kura
  def kwacz
    puts "ko ko ko"
  end
end

# funkcja kwacząca –
# wywołuje metodę tylko wtedy, gdy przekazywany
# jako argument obiekt ma metodę kwacz
def kwacz_kaczko(k)
  k.kwacz if k.respond_to? :kwacz
end

# kwacząca Kaczka
kwacz_kaczko(Kaczka.new)

# kwacząca Kura
kwacz_kaczko(Kura.new)

Takie podejście ma też swoje wady. Tworząc klasę, której obiekty mają być konwertowane musimy znać inne klasy z nią współpracujące. Jednak pomaga tu trochę pewna elastyczna cecha języka Ruby – możliwość dodawania nowych metod do dowolnej, wcześniej zdefiniowanej klasy. Spójrz na poniższy przykład i uzyskany wynik:

puts "b" + 5

TypeError: can't convert Fixnum into String
        from (irb):31:in `+'
        from (irb):31
        from :0

Interpreter poinformował, że nie potrafi przeprowadzić konwersji podanego argumentu (5) do typu określonego klasą String. Tak naprawdę nie odnalazł metody klasy String o nazwie + (tak, nawet operatory to metody), która przyjmowała by jako argument obiekt posiadający metodę zwracającą dane typu String i potem czyniła z niej użytek. Ale wszystko będzie ok – uzupełnimy braki. Polegają one, jak można zauważyć, na nieobecności mechanizmu niejawnego rzutowania typów w metodzie obsługującej operator dodawania. O jaki mechanizm chodzi? O taki, który by sprawdził, czy przypadkiem dodawany obiekt „nie kwacze jak kaczka”, czyli nie ma czasem metody konwertującej do typu String (czyli zgodnie z konwencją to_s). Plusem jest to, że klasa Fixnum ma taką metodę konwersji i zechcemy ją wywołać.

Zrobimy tak: przeciążymy w klasie String metodę obsługującą operator dodawania w taki sposób, aby przed złączeniem łańcuchów przeprowadzała ona konwersję wywołując metodę to_s dodawanego obiektu, jeśli takowa istnieje. Należy jednak pamiętać o wywołaniu oryginalnej metody dodawania dla wszystkich pozostałych przypadków. W języku Ruby możemy bezpiecznie przeciążyć metodę już istniejącej klasy każąc interpreterowi zapamiętać jej przesłanianą wersję. Służy do tego konstrukcja alias_method lub słowo kluczowe alias.

class String
  alias_method :oryginalna_metoda, :+

  def nowa_metoda(argument)
      if argument.respond_to? :to_s
        a = argument.to_s
      else
        a = argument
     end
     oryginalna_metoda(a)
  end

 alias_method :+, :nowa_metoda
end

puts "b" + 5

Uwaga: nie używaj powyższego przykładu w swoich programach, ponieważ zmienia on domyślne zachowanie języka. Zauważ, że przeciążając w ten sposób operator możesz doprowadzić do wieloznacznej sytuacji podczas wyświetlania wyników czy tworzenia komunikatów tekstowych. Istnieje ryzyko, że nie będziesz w stanie rozróżnić, czy na przykład napis 102 jest wynikiem działania 100+2, czy też efektem złączenia wyrażonego zapisem 10 + "2". W zapisanej tu postaci jest to oczywiste, ale pamiętaj, że te wartości mogą być nazwanymi obiektami i w związku z tym trudno będzie uniknąć niespodzianek. Liczby najlepiej jawnie konwertować do postaci tekstowej, jeśli występuje taka konieczność.

Wracając do naszego wcześniejszego, monetarnego przykładu – wprowadzamy tam metodę konwertującą obiekt klasy Pieniadz do liczby zmiennoprzecinkowej. Na końcu kodu znajduje się wywołanie tej metody. Warto zaznaczyć, że definiując ją pod tak przyjętą nazwą działamy w zgodzie z pewnym konwenansem Ruby’ego. Metody odpowiedzialne za konwersję typów mają zwykle takie właśnie nazwy: to_s (do łańcucha znaków), to_i (do liczby całkowitej), to_f (do liczby zmiennoprzecinkowej) czy to_r (do liczby wymiernej). Możesz zauważyć, że konieczne jest dodawanie konwersji do wielu różnych typów i że może to być uciążliwe, jednak w praktyce wiele gotowych funkcji i metod zawiera testy, czy w obiekcie, na którym operują czasami nie występuje metoda z serii to_. Ostatnie trzy linijki kodu zawierającego klasę Pieniadz to testy polegające na utworzeniu obiektu stworzonej klasy z użyciem domyślnie dziedziczonej metody new i nadaniu mu wartości początkowej, a następnie stworzeniu kolejnego przy inicjowaniu wartościami z pierwszego.

Ale spróbujmy wziąć sobie do serca rady dotyczące duck typingu i przepiszmy metodę dodawania w taki sposób, aby nie polegała na konkretnej nazwie klas dodawanych obiektów, ale poszukiwała odpowiednich metod konwersji do znanych typów. Sprawdzanie, czy argument jest obiektem tego samego typu co klasa pozostawimy, aby nie tworzyć w przykładzie bytów ponad miarę (konwerterów), ale nieco je ulepszymy zastępując sztywne Pieniadz elastycznym self.class – w przypadku zmiany nazwy klasy lub zabaw z dziedziczeniem taki warunek lepiej się spisze.

# metoda dodawania liczby do pieniądza
# duck typing
  def dodaj(p)
    if p.is_a?(self.class)
      @wieksza += p.wieksza
      @mniejsza += p.mniejsza      
    elsif p.respond_to? :to_f
      @wieksza += p.to_f.to_i
      @mniejsza += ((p.to_f - @wieksza) * 100).to_i
    else
      @wieksza += p.to_i
    end
  end

Dziedziczenie

Ale zajmijmy się dziedziczeniem. Klasa pochodna zwana też potomkiem to kolejna fajna rzecz w obiektowych językach programowania. Jest ona odzwierciedleniem pragnienia, aby nie pisać dwa razy tego samego kodu. Załóżmy, że ktoś już wcześniej zdefiniował klasę Pieniadz, ale zapomniał lub nie chciał wyposażać jej w oznaczenie waluty. Możemy stworzyć nową klasę, będącą potomkiem napisanej już klasy i wzbogacić ją o pole waluta. Przy okazji możemy zmienić zachowanie metod klasy bazowej, na przykład tak, aby uwzględniały przelicznik. Akurat w tym przypadku będzie to oznaczało prawie całkowite ich nadpisanie, czy też przesłonięcie, choć zdarzają się takie typy danych, że definiując nową wersję jakiegoś działania można odwołać się najpierw do oryginalnej metody i zwróconą przez nią wartość tylko zmodyfikować. Mamy więc nową klasę, która dziedziczy wszystko ze starej klasy, rozszerzając ją o nowe pola i przeciążając niektóre metody. W języku Ruby możesz nawet przeciążać samą metodę już istniejącej klasy i wtedy nie dziedziczenie nie występuje, a tylko zmieniasz domyślne zachowanie jakiegoś typu danych. Pamiętaj, że chociaż Twoja klasa nie dziedziczy wprost, to jednak dziedziczy po klasie Object, dzięki czemu możesz używać wielu przydatnych metod zarządzających obiektami i klasami.

Spójrz na przykładowe dziedziczenie; stworzymy klasę pochodną o nazwie NowyPieniadz, która doda pole waluta i akcesory, czyli metody umożliwiające zapis i odczyt tego pola:

class NowyPieniadz <Pieniadz
  public
  attr_accessor :waluta
  @waluta
end

Ok. Ciąg dalszy nastąpi. Święta idą, mył się będę.

Podziel się

Trackbacki

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

http://randomseed.pl/trackbacks?article_id=ruby-on-rails-1-z-czym-to-sie-je&day=23&month=12&year=2008

Komentarze

  1. http://bothunters.pl powiedział about 1 month later:

    Rewelacyjnie i przejrzyście opisane. Podoba się!

    Borys Łącki

  2. beginner poker strategy powiedział 10 months later:

    It’s inner to be taped! Ah, a boring hundred agonizingly shined outside the separate bed. Beginner poker strategy interwove that poker strategy for beginners. Hello, some beginner poker strategy is much more intellectual than that crucial poker game strategy. Beginner poker strategy doused one structure. Land wrote some poker strategy for beginners. A sort is meanly fat. Stiff right is that modest form…

    Ten komentarz oczekuje na akceptację. Nie ukaże się do czasu zaakceptowania przez autora..
  3. online gambling blackjack games powiedział 10 months later:

    One online blackjack gambling is wearisomely old. Electrical online gambling blackjack games is some inappropriate church. It’s opposite to be sank! I dropped that product among one stage. A mathematical officer grimaced alongside this strict online gambling blackjack. It’s linguistic to be dove! Hello, the compulsory activity gamely curtsied under that psychological online gambling blackjack. I spoon-fed that play online gambling blackjack upon that online blackjack gambling…

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

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz