Dynamikę można rozumieć jako ruch czy przeobrażenie - i tego wytłumaczenia chciałbym tu użyć. Ruby, podobnie jak Java, jest językiem obiektowym. Inaczej jednak niż w Javie, raz utworzony obiekt może zmieniać nie tylko swój stan, ale i zestaw komunikatów, które przyjmuje. Prościej i dosadniej rzecz ujmując, w Javie jedyne, co może zmienić się w obiekcie to zawartość jego pól. Natomiast w Ruby, oprócz tego możliwe jest dynamiczne (czyli w trakcie wykonywania programu) dokładanie nowych oraz zmiana czy usuwanie istniejących metod i pól.
Taka funkcjonalność języka daje programiście duże możliwości. Bardzo chętnie skorzystali z nich między innymi twórcy frameworku Rails. Przykładowo w warstwie modelu (bazy danych), definiując własną klasę dziedziczącą z ActiveRecord::Base, otrzymujemy za darmo cały zestaw "magicznych" metod. Dlaczego magicznych? Ponieważ można by długo i nieskutecznie szukać np. źródła dla metody find_by_author klasy Book (reprezentującej dane z tabeli book w bazie danych - zakładamy tu że tabela ta ma kolumnę author). A mimo to można z niej od razu skorzystać:
my_favourite = Book.find_by_author "Terry Prattchet"Jak to możliwe? Oto co dzieje się w momencie wywołania takiej metody:
- do obiektu Book wysyłany jest komunikat find_by_author (to po prostu określenie wywołania metody, używane w świecie Ruby) z parametrem "Terry Prattchet"
- obiekt Book nie posiada żądanej metody, więc wysyłany jest komunikat method_missing (to standardowa metoda klasy Object, z której dziedziczą wszystkie inne klasy w Ruby; jej domyślna implementacja rzuca wyjątek NoMethodError)
- wykonywana jest method_missing przeciążona w klasie ActiveRecord::Base - i tu dynamika Ruby wykorzystana jest w pełni, bo na podstawie informacji, że próbowano wykonać metodę o nazwie zaczynającej się od "find_by" oraz tego, że klasa Book ma pole "author", w locie tworzona jest nowa metoda find_by_author, której implementacja to wywołanie gotowej uniwersalnej metody ActiveRecord::Base.find z odpowiednimi parametrami
Co najważniejsze, nie trzeba wcale sięgać po własne konstrukcje z dziedziny metaprogramowania czy Domain-Specific Languages, aby używać i korzystać w pełni z dynamicznej natury Ruby. Oto prosty przykład, klasa Book, tym razem niezwiązana z ActiveRecord::Base:
class BookA oto jej odpowiednik w języku Java:
attr_accessor :author, :title
end
class Book {Wbrew pozorom, attr_accessor nie jest jakimś udziwnionym słowem kluczowym do definiowania pól razem z metodami dostępowymi. To najzwyklejsza metoda z modułu Module, dostępna dla każdego obiektu w Ruby. Rezultat jej wykonania to utworzenie w takim obiekcie pola o zadanej nazwie oraz metod: pobierającej i ustawiającej wartość tego pola. Dokładnie to samo można uzyskać w sposób zbliżony do Javy:
private String author;
private String title;
public String getAuthor() { return author; }
public void setAuthor(String value) { author = value; }
public String getTitle() { return title; }
public void setTitle(String value) { title = value; }
}
class BookOsobiście nie mam wątpliwości, który sposób jest przyjemniejszy w użyciu. Ruby pozwala na pisanie szybciej i wygodniej, więc dlaczego z tego nie skorzystać?
def author
@author
end
def author= value
@author = value
end
def title
@title
end
def title= value
@title = value
end
end