You are on page 1of 39

Ruby.

Szybki start
Autor: Larry Ullman
ISBN: 978-83-246-2258-0
Tytu oryginau: Ruby: Visual QuickStart Guide
Format: 170x230, stron: 448

Naucz si Jzyka Ruby, aby prosto i szybko przygotowa kad aplikacj


Jak tworzy i uruchamia skrypty w jzyku Ruby?
Jak instalowa bibliotek RubyGems i zarzdza ni?
Jak zbudowa projekt Rails?

Ruby to dynamiczny i niezwykle elastyczny jzyk programowania. Dziki prostemu


kodowi jest on take przystpny i atwy w nauce. Pozwala na zmian elementw jzyka
podczas pracy programu. Co wicej na najwyszym stopniu zawansowania aplikacje
napisane w tym jzyku mog wykorzystywa refleksj, czyli zdolno do samoanalizy.
Biblioteka RubyGems zawiera niestandaryzowany, ale bardzo rozbudowany kod, a poza
tym udostpnia dodatkowe narzdzia, co znacznie przyspiesza prace nad tworzeniem
dowolnej aplikacji.
Ksika Ruby. Szybki start zawiera wszystkie potrzebne wiadomoci, podane tak,
aby szybko i sprawnie nauczy si tego jzyka bez obciania pamici zbdnymi
szczegami czy niezrozumiaym technicznym argonem. Zamieszczone tu instrukcje,
z dodatkowymi objanieniami graficznymi, krok po kroku pokazuj na przykad kod, jaki
naley wpisa z klawiatury. Z podrcznikiem w rku nauczysz si m.in. uywa wtkw,
konfigurowa baz danych, instalowa bibliotek RubyGems i zarzdza ni.
Reasumujc z t ksik moesz od razu zacz prac i korzysta z moliwoci
jzyka Ruby do realizacji wielu zada programistycznych.
Dokumentacja jzyka Ruby
Uruchamianie skryptw i pobieranie danych
Tablice, zakresy i hasze
Struktury sterujce
Tworzenie metod
Klasy i dziedziczenie
Moduy
Wyraenia regularne
Debugowanie i obsuga bdw
Katalogi i pliki
Bazy danych
Ruby on Rails
Programowanie dynamiczne

Szybki start w wiat jzyka Ruby!

Spis treci
Spis treci

Rozdzia 1.

Wstp

Zaczynamy

17

Instalacja w systemie Windows ........................................................................... 18


Instalacja w systemie Mac OS X .......................................................................... 20
Testowanie instalacji ............................................................................................. 23
Dokumentacja jzyka Ruby .................................................................................. 25
Interaktywna powoka jzyka Ruby ..................................................................... 28
Konfiguracja powoki irb ...................................................................................... 31
Rozdzia 2.

Proste skrypty

35

Rozdzia 3.

Typy podstawowe

Spis treci

Tworzenie najprostszego skryptu ......................................................................... 36


Uruchamianie skryptw w Windows ..................................................................... 38
Uruchamianie skryptw z wiersza polece ......................................................... 40
Skrypty wykonywalne ........................................................................................... 42
Wywietlanie tekstu .............................................................................................. 44
Pobieranie danych ................................................................................................. 46
Komentarze ........................................................................................................... 48

51

Liczby .................................................................................................................... 52
Obliczenia arytmetyczne ...................................................................................... 54
Metody operujce na liczbach .............................................................................. 56
acuchy znakw .................................................................................................. 59
Interpolacja i zastpowanie .................................................................................. 61
Podstawowe metody operujce na acuchach znakw ..................................... 64
acuchy znakowe wielowierszowe .................................................................... 66
Stae ....................................................................................................................... 69
Data i czas .............................................................................................................. 71
Rozdzia 4.

Tablice, zakresy i hasze

75

Tworzenie tablic .................................................................................................... 76


Podstawowe metody klasy Array .......................................................................... 79
Dodawanie elementw ......................................................................................... 83

Spis treci
Usuwanie elementw ............................................................................................ 86
Tablice i acuchy znakw .................................................................................... 88
Uywanie zakresw ............................................................................................... 90
Tworzenie haszw ................................................................................................. 93
Standardowe metody klasy Hash ........................................................................... 95
Rozdzia 5.

Struktury sterujce

97

Operatory ............................................................................................................... 98
Podstawowe instrukcje warunkowe ................................................................... 101
Instrukcje warunkowe zoone .......................................................................... 105
Operator warunkowy .......................................................................................... 108
Instrukcja case ..................................................................................................... 112
Ptle ..................................................................................................................... 116
Iteratory liczbowe ............................................................................................... 120
Iteratory kolekcji ................................................................................................. 123
Rozdzia 6.

Tworzenie metod

127

Spis treci

Proste metody ...................................................................................................... 128


Zwracanie wartoci ............................................................................................. 131
Pobieranie argumentw ...................................................................................... 135
Domylne wartoci argumentw ........................................................................ 138
Uywanie self ...................................................................................................... 140
Argumenty o zmiennej dugoci ........................................................................ 143
Metody i bloki ..................................................................................................... 147
Rozdzia 7.

Klasy

151

Proste klasy .......................................................................................................... 152


Zmienne instancji ................................................................................................ 156
Akcesory .............................................................................................................. 159
Konstruktory ........................................................................................................ 162
Definiowanie operatorw ................................................................................... 166
Metody specjalne ................................................................................................ 175
Walidacja i duck typing ...................................................................................... 180
Rozdzia 8.

Dziedziczenie i caa reszta

185

Proste dziedziczenie ........................................................................................... 186


Nadpisywanie metod .......................................................................................... 191
Metody czone ................................................................................................... 194
Kontrola dostpu ................................................................................................. 198
Zmienne klasy ..................................................................................................... 204
Metody klasy ....................................................................................................... 207

Spis treci
Rozdzia 9.

Moduy

213

Moduy jako przestrzenie nazw ......................................................................... 214


Moduy jako klasy mieszane ............................................................................... 218
Doczanie plikw ............................................................................................... 222
Standardowa biblioteka jzyka Ruby .................................................................... 227
Rozdzia 10.

Wyraenia regularne

229

Przeprowadzanie dopasowa ............................................................................. 230


Definiowanie prostych wzorcw ........................................................................ 233
Uywanie kotwic ................................................................................................. 236
Uywanie kwantyfikatorw ................................................................................ 239
Uywanie klas znakw ........................................................................................ 242
Uywanie modyfikatorw ................................................................................... 246
Wyszukiwanie dopasowa .................................................................................. 248
Przeprowadzanie podstawie ............................................................................. 252
Rozdzia 11.

Debuggowanie i obsuga bdw

257

Rozdzia 12.

RubyGems

Spis treci

Uywanie debuggera Ruby ................................................................................ 258


Obsuga wyjtkw ............................................................................................... 264
Obsuga wyjtku w zalenoci od jego typu ...................................................... 268
Zgaszanie wyjtkw ........................................................................................... 271
Testowanie jednostkowe (Unit Testing) ................................................................. 276

283

Instalacja RubyGems .......................................................................................... 284


Instalacja i zarzdzanie bibliotekami ..................................................................... 287
Korzystanie z pakietw ....................................................................................... 291
Pakiet creditcard ................................................................................................. 294
Pakiet Highline ................................................................................................... 296
Pakiet RedCloth .................................................................................................. 302
Rozdzia 13.

Katalogi i pliki

305

Podstawy .............................................................................................................. 306


Dostp do zawartoci katalogu ........................................................................... 309
Waciwoci katalogw i plikw ......................................................................... 312
Uprawnienia ........................................................................................................ 315
Tworzenie, przenoszenie, kopiowanie i usuwanie ............................................ 319
Odczytywanie danych z plikw .......................................................................... 322
Zapisywanie danych w plikach ........................................................................... 325
FasterCSV ........................................................................................................... 329

Spis treci
Rozdzia 14.

Bazy danych

333

Zaczynamy ........................................................................................................... 334


Wykonywanie prostych zapyta ......................................................................... 337
Wstawianie rekordw ......................................................................................... 339
Pobieranie rekordw ........................................................................................... 344
Wykonywanie transakcji ..................................................................................... 348
Rozdzia 15.

Sie

353

Tworzenie serwera gniazd .................................................................................. 354


Uywanie wtkw ............................................................................................... 357
Tworzenie klienta gniazd .................................................................................... 362
Poczenia HTTP ................................................................................................ 365
Obsuga rde RSS ............................................................................................ 369
Rozdzia 16.

Ruby on Rails

373

Spis treci

Elementarz Rails ................................................................................................. 374


Zaczynamy ........................................................................................................... 376
Konfiguracja bazy danych ................................................................................... 381
Tworzenie bazy danych ...................................................................................... 384
Wyprbowywanie serwisu .................................................................................. 389
Dostosowywanie modeli ..................................................................................... 391
Dostosowywanie widokw ................................................................................. 395
Dostosowywanie kontrolerw ............................................................................ 403
Rozdzia 17.

Programowanie dynamiczne

409

Integracja z systemem operacyjnym .................................................................. 410


Skaone dane ....................................................................................................... 414
Poziomy bezpieczestwa .................................................................................... 418
Elementy proc i lambda ..................................................................................... 422

Skorowidz

429

Klasy

Rozdzia 7. Klasy [RS2]


Klasy s fundamentem programowania
zorientowanego obiektowo (OOP). Klasa jest
wzorcem, obiekt jest instancj klasy. Poniewa
w jzyku Ruby wszystko jest obiektem, nawet
prosty przykad Witaj, wiecie! korzysta z obiektw.
Mimo e Ruby dostarcza mnstwo wbudowanych
funkcji poprzez wbudowane klasy, w kocu bdziesz
chcia tworzy wasne.
Rozdzia ten przedstawia podstawy tworzenia
i uywania klas w jzyku Ruby. Znajdziesz w nim
wiadomoci o skadni klas, zmiennych klas oraz
jak pisa kilka rnych rodzajw metod, ktre s
wykorzystywane w klasach. Wprowadzone zostan
rwnie dwa pojcia, ktre odrniaj Ruby od
wielu innych jzykw zorientowanych obiektowo.
Pierwszym jest atwo, z jak moesz zmienia
istniejce klasy. Drugim jest duck typing. Zostanie
ono opisane na kocu rozdziau.

151

Klasy

Rozdzia 8., Dziedziczenie i caa reszta, jest


z niniejszym powizany. Gwna uwaga skupia si
w nim na definiowaniu klas, ktre wywodz si
z innych klas.

Rozdzia 7.

Proste klasy
Programowanie proceduralne podchodzi do
aplikacji jako do krokw, ktre naley wykona.
Przeciwnie do niego programowanie obiektowe
skupia s na danych przetwarzanych w aplikacji.
Stosujc takie podejcie, klasa musi reprezentowa
dane aplikacji w sensie danych, ktre s
przechowywane, jak i zada do wykonania.
Informacja moe by reprezentowana przez
zmienne (nazywane atrybutami lub
waciwociami klasy) oraz funkcje (w klasach
nazywanymi metodami). Jako teoretyczny
przykad wemy osob: posiada ona atrybuty
imi, wiek itp. oraz metody je, pi itd.
Podstawow definicj klasy rozpoczyna sowo
kluczowe class, po czym nastpuje nazwa klasy.
Nastpnie jest jej kod (ciao klasy), definicj klasy
koczy sowo kluczowe end.
class NazwaKlasy
# kod klasy
end

Nazwa klasy moe zawiera litery, cyfry i znaki


podkrelenia, zazwyczaj jednak zawiera tylko
litery. Nazwy klas traktowane s jako stae, wic
musz rozpoczyna si od wielkiej litery.
Przyjmuje si, e uywaj wielkich liter
do oddzielania wyrazw w nazwach klas:
NazwaKlasy, nie nazwaKlasy czy Nazwa_klasy.
Metody wewntrz klasy definiuje si tak samo,
jak to zostao opisane w rozdziale 6., Tworzenie
metod. Definicje metod zazwyczaj s wcite
o dwie spacje, lecz skadnia tego nie wymaga.
Na przykad tak bardzo kochasz przykad Witaj,
wiecie!, e uwaasz, i zasuguje na wasn klas:
class HelloWorld
def say_hello
puts "Witaj, wiecie!"
end
end

Po utworzeniu klasy moesz stworzy zmienn


jej typu, uywajc NazwaKlasy.new. Wiersz ten
korzysta z wywoania metody new danej klasy,
kada klasa posiada tak metod automatycznie,
bez koniecznoci definiowania jej przez Ciebie.

Proste klasy

hw = HelloWorld.new

Klasa Struct
Jeli potrzebujesz przechowa wiele informacji w strukturze podobnej do klasy, ale nie musisz
definiowa adnych metod, moesz uy klasy Struct. Aby j zdefiniowa, wpisz Struct.new,
jako parametry podajc nazw struktury oraz zmiennych (jako symbole), jakie powinna zawiera:
Book = Struct.new('Book', :title, :author, :isbn)

Teraz moesz utworzy zmienn tego typu:


rubyvqs = Book.new('Ruby: VQS', 'Larry Ullman', '978-0-321-55385-0')

Od tego momentu moesz uywa skadni z kropk do dostpu do danych:


puts rubyvqs.title # Ruby: VQS

Alternatywnie moesz traktowa t zmienn jak hasz:


puts rubyvqs[:title] # Ruby: VQS

152

Klasy
Uyj skadni z kropk do wywoania metod klasy
(rysunek 7.1):
hw.say_hello

Rysunek 7.1. Po utworzeniu nowej klasy Ruby


zwraca nil. Kiedy utworzony zostanie nowy obiekt
klasy, Ruby zwraca nazw typu oraz referencj
do obiektu (0x2939b88). Metody klasy mog by
wywoywane na jej obiektach

Ruby posiada wiele metod, ktre mog by uyte


przez kad klas. Podobnie jak metoda new, te inne
metody s automatycznie dziedziczone przez klasy,
ktre utworzye (rozdzia 8., Dziedziczenie i caa
reszta, zajmuje si szczegowo dziedziczeniem).
Na przykad metoda class zwraca nazw klasy,
do ktrej naley dany obiekt:
hw.class # HelloWorld

Aby wywietli list metod, jakie posiada klasa, uyj


NazwaKlasy.methods. Aby wywietli list metod, jakie
posiada dany obiekt, wpisz nazwa_obiektu.methods
(rysunek 7.2) lub NazwaKlasy.instance_methods.
W biecym rozdziale utworzysz wiele rnych
klas niektre w celu lepszego zrozumienia zasad
programowania obiektowego, inne bd bardziej
uyteczne. Dla uproszczenia pocztkowe przykady
bd napisane przy uyciu powoki irb, pniejsze
przykady bd skryptami w osobnych plikach.
Moesz napisa skrypty dla kadego prezentowanego
przykadu, jeli oczywicie chcesz (tak jak opisano
w rozdziale 2., Proste skrypty).

Proste klasy

Rysunek 7.2. Pierwsza cz wywietla 75 metod, ktre mog by uyte z klas HelloWorld
(w ktrej zdefiniowano tylko jedn metod). Druga cz wywietla 42 metody,
ktre mog by wywoane na obiekcie HelloWorld

153

Rozdzia 7.
Aby utworzy klas:
1. Rozpocznij definiowanie klasy:
class Dog

Klasa nazywa si Dog (pies) i posuy


do uproszczonego reprezentowania, eee,
psa w kodzie jzyka Ruby.
2. Dodaj kilka metod:
def speak
"hau"
end
def play
"przynie"
end
def sleep
"pij"
end

Te trzy proste metody reprezentuj czynnoci,


ktre moe wykonywa pies. Kada z nich
zwraca pojedynczy wyraz jako cig znakw.
Wicej o definiowaniu metod mona przeczyta
w rozdziale 6., Tworzenie metod.
3. Zakocz definiowanie klasy (rysunek 7.3):
end

Proste klasy

Gdybym pisa ten kod jako skrypt Ruby,


doczybym tutaj komentarz, aby zaznaczy, e
ten end koczy definicj klasy (w przeciwiestwie
do koca definicji metody).

154

Rysunek 7.3. Definicja klasy Dog

Klasy
4. Uyj klasy (rysunek 7.4):
trixie = Dog.new
trixie.speak
trixie.play
trixie.sleep

Pierwszy wiersz tworzy nowy obiekt typu Dog.


Obiekt jest zmienn o nazwie trixie. Jego
metody mog by wywoane za pomoc skadni
z kropk. Poniewa kada z metod zwraca cig
znakw, wynikiem wywoania kadej z nich jest
po prostu cig znakw. Jako alternatyw moesz
wypisa wynik wywoania kadej metody:
puts trixie.speak

Wskazwki
Metody definiowane na zewntrz klasy, jak te

z rozdziau 6., Tworzenie metod, automatycznie


s definiowane jako metody klasy Object.
Poniewa kada klasa automatycznie dziedziczy
po klasie Object, metody te s dziedziczone
przez kad klas.
Metody zdefiniowane wewntrz klasy, takie
jak speak, play czy sleep, nazywane s

metodami instancji (ang. instance methods).

Proste klasy

Rysunek 7.4. Utworzenie obiektu klasy Dog i wywoanie kilku jego metod

155

Rozdzia 7.

Zmienne instancji

Taka wersja klasy bdzie uywana


w nastpujcy sposb (rysunek 7.5):

Zmienne definiowane poza klas s zmiennymi


trixie = Dog.new
lokalnymi, tak jak te definiowane i uywane
trixie.set_name('trixie')
w poprzednich rozdziaach. Zmienne definiowane
puts "Mj pies wabi si #{trixie.get_name}."
wewntrz klasy to zmienne instancji (ang. instance
Oczywicie kada metoda klasy moe korzysta
variables). Nale one do klasy, dlatego nale te
ze zmiennej instancji:
do utworzonych obiektw danej klasy. Zmienne
def sleep
instancji podlegaj tym samym reguom nazewnictwa
"#@name pi"
co inne zmienne, wyjtkiem jest to, e ich nazwa
end
zaczyna si od znaku @.
W metodzie sleep pokazano nowy przykad
W klasie Dog zmienne instancji mog przechowywa
interpolacji (wstawienie wewntrz cigu
imi, wiek, ras, pe itd. poszczeglnego psa.
znakw wartoci zwrconej w wyniku
W klasach czsto tworzone s metody suce do
polecenia). Aby wstawi zwyke zmienne,
przypisywania wartoci danej zmiennej instancji musisz uy #{nazwa_zmiennej}, ale by wstawi
(ang. setter method) lub pobierania jej zawartoci warto zmiennej instancji, moesz uy
(ang. getter method). W tym celu mona w klasie
#@nazwa_zmiennej.
Dog utworzy takie dwie metody:

Zmienne instancji

def set_name(n)
@name = n
end
def get_name
@name
end

Rysunek 7.5. Jedna metoda setter i jedna metoda getter zostay dodane
do klasy Dog, by mie dostp do zmiennej @name

156

Klasy
Aby uywa zmiennych instancji:
1. Rozpocznij definiowanie nowej klasy:
class Circle

Klasa Circle bdzie reprezentowa koa.


Rysunek 7.6. Pocztek definicji klasy Circle
z metodami setter i getter dla zmiennej @radius

2. Zdefiniuj metod setter:


def set_radius(r)
@radius = r
end

Metoda ta przypisuje zmiennej @radius warto


przekazan podczas wywoywania teje metody.
3. Zdefiniuj metod getter (rysunek 7.6):
Rysunek 7.7. Koniec definicji klasy Circle,
jedna z metod zwraca pole koa, druga jego obwd

def get_radius
@radius
end

Metoda ta zwraca dugo promienia koa.


Pamitaj, e wynik ostatniego wykonanego
polecenia w metodzie bdzie przez ni zwrcony
(patrz rozdzia 6., Tworzenie metod). Uycie
nazwy zmiennej jako ostatniego polecenia
w metodzie spowoduje zwrcenie wartoci tej
zmiennej.
4. Zdefiniuj jeszcze dwie metody:

Metody te zwracaj obliczone pole koa (ang. area)


oraz obwd koa (ang. perimeter). Do oblicze
wykorzystuj zmienn @radius oraz pochodzc
z moduu Math sta PI.
Wykorzystany do obliczania pola operator
potgowania (**) posiada wyszy priorytet ni
operator mnoenia, jeli jednak chcesz, by
wszystko byo bardziej jednoznaczne, moesz
napisa:
Math::PI * (@radius**2)

5. Zakocz definiowanie klasy (rysunek 7.7):


end

157

Zmienne instancji

def area
Math::PI * @radius**2
end
def perimeter
2 * Math::PI * @radius
end

Rozdzia 7.
6. Utwrz nowe koo oraz ustaw dugo promienia:
c = Circle.new
c.set_radius(4.59)

W pierwszym wierszu tworzony jest nowy obiekt


klasy Circle. W drugim wierszu wywoujemy
metod set_radius, ktra zmiennej @radius
przypisuje warto 4.59.

Wskazwki
Zmienne instancji s dostpne jedynie

dla metod instancji. Oznacza to na przykad,


e nie moesz odwoywa si do zmiennej
@radius spoza klasy Circle.
W jzyku Ruby przed uyciem zmiennych

7. Wywietl waciwoci koa (rysunek 7.8):


puts "Koo o promieniu #{c.get_radius} ma
obwd dugoci #{c.perimeter}, a jego pole
wynosi #{c.area}."

Zmienne instancji

Trzy metody obiektu s wywoywane, eby


wywietli odpowiednie wartoci.

Rysunek 7.8. Przykadowe uycie klasy Circle

158

nie musisz ich deklarowa ani inicjalizowa.


Dotyczy to take zmiennych instancji.
W rzeczy samej nie powiniene prbowa
deklarowa ani inicjalizowa zmiennych
instancji inaczej ni za pomoc metod.
Nie rb czego takiego:
class Person
@name = null # le!
def set_name(n)
@name = n

Klasy

Akcesory
Jak ju wspomniaem kilka razy w tej ksice,
jzyk Ruby zosta stworzony po to, aby wszystko
byo dla programisty atwe (albo przynajmniej
atwiejsze ni dotychczas). Skoro obecno
w klasach metod setter i getter jest powszechna,
jzyk Ruby posiada skrt uatwiajcy ich
tworzenie. Podczas definicji klasy przed
definiowaniem jakichkolwiek metod moesz uy
sw kluczowych attr_reader oraz attr_accesor,
aby wskaza zmienne, dla ktrych powinny
istnie metody setter i getter.
class NazwaKlasy
attr_accesor :var1, :var2, :var3
# inne metody
end

Jak wida w powyszym przykadzie, uywasz


sowa kluczowego attr_accessor lub
attr_reader, a nastpnie podajesz list
symboli reprezentujcych zmienne instancji
oddzielone przecinkami. Tak wic definicja
klasy Dog moe zaczyna si w ten sposb:
class Dog
attr_accessor :name, :breed, :gender

zmiennej z listy. Po zakoczeniu definiowania


klasy Dog moesz wic napisa tak (rysunek 7.9):
d = Dog.new
d.name = 'Sara'
d.breed = 'York'
d.gender = 'suczka'
puts "Moj pies, #{d.name}, to #{d.gender} rasy
#{d.breed}."

Wiersze te pokazuj, jak atwo moesz instancji


przypisa zmiennym wartoci (to znaczy
ustawi je) oraz uzyska ich wartoci (to znaczy
pobra je, tak jak w powyszej instrukcji puts).
Jeli uyjesz sowa kluczowego attr_reader,
tylko metody getter zostan utworzone,
oznacza to, e moesz zrobi to
puts nazwa_obiektu.nazwa_zmiennej

ale nie to
nazwa_obiektu.nazwa_zmiennej = warto

Zmiemy teraz klas Circle, korzystajc z opisanej


powyej techniki. Jeli do pisania tych przykadw
korzystasz z powoki irb, nastpne kroki prowadz
do ciekawego spostrzeenia: w jzyku Ruby
moesz modyfikowa istniejce klasy w locie.

Akcesory

Zauwa, e skadnia jest nastpujca :name,


:breed i :gender, a nie @name, @breed, i @gender.

Jeli uyjesz attr_accessor, waciwe metody


setter i getter zostan utworzone dla kadej

Rysunek 7.9. Dziki uyciu attr_accesor atwo jest przypisywa


i odczytywa wartoci zmiennych instancji

159

Rozdzia 7.
Aby uy akcesorw:
1. Dodaj akcesor do klasy Circle:
class Circle
attr_accessor :radius
end

Te trzy wiersze oznaczaj, mwic wprost:


we istniejc definicj klasy Circle i zmie j,
dodajc metody setter i getter dla zmiennej
@radius.
Jeli do wicze wykorzystujesz powok irb
oraz ju wczeniej zdefiniowae klas Circle,
wwczas nadal posiada ona metody set_radius,
get_radius, area oraz perimeter. Jeeli nie
korzystasz z powoki irb lub wczeniej nie
zdefiniowae klasy Circle, bdziesz musia
dopisa definicje metod area i perimeter.
2. Utwrz nowe koo i ustaw dugo promienia

(rysunek 7.10):
c = Circle.new
c.radius = 123.481

Akcesory

W pierwszym wierszu tworzysz obiekt klasy


Circle. W drugim wierszu przypisujesz warto
zmiennej @radius w bardziej bezporedni
i oczywisty sposb.
3. Wywietl wymiary koa (rysunek 7.11):
puts "Koo o promieniu #{c.radius} ma obwd
dugoci #{c.perimeter} i pole wielkoci
#{c.area}."

Wiersz ten jest podobny do tego z poprzedniego


przykadu, ale teraz promie jest zwracany
poprzez wywoanie po prostu c.radius.

160

Rysunek 7.10. Maa modyfikacja klasy Circle oraz


utworzenie nowego obiektu tego typu

Rysunek 7.11. Dziki uyciu akcesora polecenie


puts moe teraz pobra dugo promienia za
pomoc c.radius

Klasy
Wskazwki
Metody setter i getter automatycznie

wygenerowane przez wiersz:


attr_accessor :nazwazmiennej

s rwnowane
def nazwazmiennej
@nazwazmiennej
end
def nazwazmiennej(warto)
@nazwazmiennej = warto
end

W rzeczywistoci sowa kluczowe attr_reader


i attr_accessor s metodami zdefiniowanymi
w klasie Module, ktrej klas potomn jest
klasa Class (patrz rozdzia 8., Dziedziczenie

i caa reszta).
W jzyku Ruby istnieje rwnie metoda attr.

W wersji 1.8 i starszych tworzy ona metod


getter dla pojedynczej zmiennej, jeli wpiszesz:
attr :nazwa_zmiennej

Uyta jak w przykadzie poniej tworzy obie


metody, getter i setter:
attr :nazwa_zmiennej, true

Metody attr_reader oraz attr_accessor tworz


proste metody getter i setter. Jeli potrzebujesz

bardziej wyrafinowanych lub elastycznych metod,


musisz napisa je sam. Na przykad bardziej
dokadna metoda setter klasy Circle moe
sprawdza, czy promie jest wikszy od zera.
Uycie metod attr_reader i attr_accessor

jest przykadem metaprogramowania (ang.


metaprogramming), kiedy to Ruby generuje kod
za Ciebie.

161

Akcesory

W wersji 1.9 jzyka Ruby attr moe by


stosowana tak jak w poprzednich wersjach
lub jako alias metody attr_reader.

Rozdzia 7.

Konstruktory
W wikszoci jzykw zorientowanych obiektowo
istnieje metoda, ktra jest automatycznie
wywoywana podczas tworzenia obiektu klasy.
W innych jzykach nazywa j konstruktorem,
w jzyku Ruby okrela si je rwnie mianem
inicjalizatora (ang. initializer), poniewa jej nazwa
to zawsze initialize. (W jzyku Java i C++
konstruktor jest metod o takiej samej nazwie
jak nazwa klasy, do ktrej naley).
class NazwaKlasy
def initialize
# zrb cokolwiek
end
end

Metoda ta bdzie automatycznie wywoana


w momencie tworzenia obiektu tej klasy za pomoc
NazwaKlasy.new. Zazwyczaj metoda initialize jest
wykorzystywana do ustawiania wartoci atrybutw
klasy, w takim przypadku przyjmuje argumenty:

Konstruktory

class Dog
def initialize(name)
@name = name
end
end
d = Dog.new('Reksio')

Zdefiniowana tutaj klasa Dog posiada tylko jedn


metod initialize. W obecnej postaci w kadym
obiekcie klasy Dog mona zmiennej @name przypisa
warto, ale nie ma sposobu jej pobrania (to znaczy
nie ma metody getter dla zmiennej @name). Jednym
z rozwiza jest uycie attr_reader, eby Ruby
utworzy metod getter za Ciebie.
class Dog
attr_reader :name
def initialize(name)
@name = name
end
end
d = Dog.new('Reksio')
puts "Mj pies wabi si #{d.name}."

162

Klasy
Aby utworzy konstruktor:
1. Rozpocznij definiowanie nowej klasy:
class Rectangle
attr_reader :height, :width

Rysunek 7.12. Pocztek definicji klasy Rectangle


z metodami getter dla jej dwch zmiennych oraz
konstruktorem, ktry przypisuje im wartoci

Nowa klasa o nazwie Rectangle (prostokt)


bdzie reprezentowa figur geometryczn.
Posiada ona dwie zmienne instancji, height
(wysoko) i width (szeroko), ktre reprezentuj
wymiary prostokta. Metody getter dla nich
zostan stworzone automatycznie dziki
zastosowaniu attr_reader.
2. Zdefiniuj konstruktor (rysunek 7.12):
def initialize(h, w)
@height, @width = h, w
end

Metoda initialize przyjmuje dwa argumenty


h i w. Za pomoc rwnolegego przypisania
warto h zostanie przypisana atrybutowi @height,
a warto w zostanie przypisana atrybutowi
@width.
3. Zdefiniuj jeszcze dwie metody:

Podobnie jak metody klasy Circle o takich


samych nazwach, te bd zwraca pole i obwd
figury.

163

Konstruktory

def area
@height * @width
end
def perimeter
(@height + @width) * 2
end

Rozdzia 7.
4. Zdefiniuj jeszcze jedn metod (rysunek 7.13):
def is_square?
if @height == @width then
true
else
false
end
end

Metoda ta zwraca warto logiczn, ktra okrela,


Rysunek 7.13. Trzy metody dodane do klasy
czy prostokt jest kwadratem (ang. square), czy
Rectangle dostarcz podstawowych informacji
nie. Stosuje si do przyjtej w jzyku Ruby
o figurze
konwencji koczenia nazw metod znakiem
pytajnika (?), jeli zwracaj wartoci logiczne
(Boolean).
5. Zakocz definicj klasy:
end

6. Utwrz obiekt typu Rectangle:


r = Rectangle.new(10, 34)

Konstruktory

Poniewa klasa posiada konstruktor oczekujcy


dwch argumentw, dwie wartoci musz by
przekazane metodzie new w momencie tworzenia
obiektu. Ten prostokt bdzie mia wysoko
10 i szeroko 34.

164

Klasy
7. Uyj metod obiektu (rysunek 7.14):
puts "Prostokt o wysokoci #{r.height}
i szerokoci #{r.width} posiada obwd
dugoci #{r.perimeter}, a jego pole
wynosi #{r.area}."
puts "Prostokt ten #{r.is_square? ? 'jest' :
'nie jest'} kwadratem."

Pierwsze polecenie puts pokazuje, jak


mona wykorzysta automatycznie stworzone
metody do pobrania wymiarw prostokta.
Polecenie to wywouje rwnie metody
perimeter i area.
Drugie polecenie puts wykorzystuje metod
is_square? z operatorem trzyargumentowym
(ang. ternary), aby wywietli Prostokt ten
jest kwadratem lub Prostokt ten nie jest
kwadratem. Wicej o skadni tego operatora
moesz przeczyta w rozdziale 5., Struktury
sterujce.

8. Utwrz nowy prostokt i przepisz wiersze

z kroku 7. (rysunek 7.15):


r = Rectangle.new(14, 14)
puts "Prostokt o wysokoci #{r.height}
i szerokoci #{r.width} posiada obwd
dugoci #{r.perimeter}, a jego pole
wynosi #{r.area}."
puts "Prostokt ten #{r.is_square? ? 'jest' :
'nie jest'} kwadratem."

Polecam wiczenia z innym prostoktem,


ktry jest rwnie kwadratem, choby po to,
eby zobaczy wynik. Moesz utworzy
nowy obiekt klasy Rectangle, aby nadpisa
istniejc zmienn r.

Konstruktory

Rysunek 7.14. Utworzenie obiektu klasy Rectangle


i wywoanie jego piciu metod

Rysunek 7.15. Utworzenie obiektu klasy Rectangle,


ktry jest rwnoczenie kwadratem

165

Rozdzia 7.

Definiowanie operatorw
Tak jak obiekty posiadaj metody suce
do wykonywania rnych zada, tak samo mog
wykorzystywa operatory arytmetyczne,
przypisania i logiczne ktrych sposb dziaania
moesz zdefiniowa. Aby to zrobi, definiujesz
metod, ktrej nazwa jest operatorem: +, -, == itd.
Operatory takie wymagaj dwch argumentw
4 - 2, cig_znakw + inny_cig_znakw, wic
waciwa metoda musi operowa na dwch
obiektach. Pierwszym obiektem bdzie operand
z lewej strony (4 i cig_znakw w przedstawionym
przykadzie). Ten obiekt jest dostpny w metodzie
poprzez odwoanie do self (patrz rozdzia 6.,
Dziedziczenie i caa reszta) lub do zmiennych
instancji klasy. Operand z prawej strony (2 i inny_
cig_znakw) powinien by przyjmowany jako
jedyny argument metody:

Definiowanie operatorw

class NazwaKlasy
def *(rh)
# Rb co z self i rh
end
end

To podstawowa skadnia implementacji operatora


mnoenia dla klasy. Od Ciebie, twrcy klasy, zaley,
co metoda powinna robi w momencie uycia
operatora. Moesz zdecydowa si na pozostawienie
operatora niezdefiniowanego, co spowoduje
wystpienie bdu, jeli kto uyje go z danym
obiektem (rysunek 7.16).
Jako przykad wemy dodawanie dwch obiektw
typu Rectangle:
r1 = Rectangle.new(10, 20)
r2 = Rectangle.new(6, 8)
puts r1 + r2

166

Rysunek 7.16. Jeli klasa nie posiada zdefiniowanej


metody dla danego operatora, uycie go z obiektami
klasy spowoduje wystpienie bdu NoMethodError

Klasy
Czy w tym przypadku wynikiem powinna by
suma pl prostoktw (200 + 48 = 248), czy suma
ich obwodw (60 + 28 =88)? By moe powinna
oblicza sum wysokoci i szerokoci prostoktw:
def +(rh)
@height += rh.height
@width += rh.width
self
end

Rysunek 7.17. Program, w ktrym wykorzystano


dwa obiekty Circle w celu demonstracji dziaania
operatorw przypisania i przyrwnania bez
koniecznoci definiowania ich w klasie

W przypadku klasy Rectangle kade podejcie


powoduje powstanie problemw. Raz jeszcze
od Ciebie zaley, jak tego typu sytuacje bd
obsugiwane, jeli w ogle: moe okaza si, e
dla danej klasy operacje dodawania, odejmowania,
mnoenia czy dzielenia nie maj sensu.
Inn decyzj, ktr naley podj, jest: czy operacja
ma zwrci nowy obiekt, czy modyfikowa istniejcy
(patrz ramka Obiekty zmieniajce same siebie
na stronie 171. W poprzednim przykadzie
modyfikowane s wartoci biecego obiektu,
a nastpnie zwracane. Aby utworzy i zwrci
nowy obiekt, metoda moe wyglda nastpujco:

Na koniec niektre obiekty posiadaj po dwa


operatory lub metody robice to samo, a rnice
si tym, e jedna modyfikuje biecy obiekt, a druga
zwraca nowy obiekt, pozostawiajc biecy bez
zmian. Jako przykad wemy klas String, ktra
posiada dwie formy czenia (konkatenacji): +,
zwracajcy nowy obiekt, oraz <<, modyfikujcy
biecy.
Klasy mog chcie definiowa wasne wersje
operatorw przypisania i logicznych. Jzyk Ruby
automatycznie generuje operatory przypisania (=)
i przyrwnania (==) (rysunek 7.17), moesz jednak
chcie zmieni ich zachowanie.

167

Definiowanie operatorw

def +(rh)
Rectangle.new(@height +rh.height, @width +
rh.width)
end

Rozdzia 7.
W przeciwiestwie do poprzednich oglny operator Tabela 7.1. Powierzchnia cakowita
porwnania, <=>, nie jest automatycznie definiowany. szeroko dugo
Definiujc metod <=>, moesz sprawi, e obiekty 4
30
danego typu bdzie mona porwnywa i sortowa. 5.5
22
Po prostu zwracaj -1, jeli lewy operand jest
6
14
mniejszy, 1, jeli prawy operand jest mniejszy, i 0,
Powierzchnia cakowita
jeli s rwne. Na przykad jako podstaw do
porwnywania dwch obiektw typu Rectangle
wemy ich pola:

powierzchnia
120
121
84
325

def <=>(rh)
if self.area < rh.area then -1
elsif self.area > rh.area then 1
else 0 end
end

Definiowanie operatorw

Jakoz ycia wzity przykad wykorzystania tej


wiedzy podam moje hobby: stolark1. Kiedy
zaczynam nowy projekt, musz wiedzie, ile desek
posiadam, eby wiedzie, czy ich wystarczy.
Powierzchnia cakowita desek ktre posiadam
lub ktrych projekt wymaga jest sum
powierzchni pojedynczych deseczek. Tabela 7.1
zawiera przykadowe dane.
Podczas definiowania klasy przyjem kilka zaoe.
Zakadam, e dwie deski s takie same, jeli maj
t sam powierzchni. Liczba desek jest raczej
nieistotna, dwie mniejsze mog posuy do
stworzenia wikszej. Po drugie, dla uproszczenia
pomijam grubo desek. W rzeczywistoci moesz
mie deski, ktre maj ptorej cala gruboci (tak
zwane 6/4), a powierzchnia, ktr moesz uzyska,
zaley od ich gruboci (zakadajc t sam kubatur).
Majc deseczki o gruboci , bdziesz mia dwa
razy wiksz powierzchni; uycie deseczek 2/4
oznacza trzy razy wiksz powierzchni. Moesz
uwzgldni to w klasie, oznacza to jedynie troch
wicej matematyki.

Aby zrozumie poniszy stolarski przykad z ycia wzity, naley si mae wytumaczenie. W oryginale
autor uywa amerykaskiej miary drewna board foot. board foot deska o dugoci jednej stopy,
szerokoci jednej stopy i gruboci jednego cala lub odpowiednik o takiej samej kubaturze. rdo:
http://www.woodweb.com/knowledge_base/What_is_a_Board_Foot.html. Tak wic board foot jest miar
objtoci (kubatury), a nie powierzchni. Z tego powodu deski o rnych grubociach posiadaj rne
powierzchnie przy takiej samej objtoci proste. Autor zbija st przyp. tum.

168

Klasy
Listing 7.1. Klasa Wood umoliwiajca dodawanie
i porwnywanie obiektw typu Wood
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

1. Utwrz nowy skrypt w edytorze tekstu lub

IDE (Listing 7.1):


# Skrypt 7.1 - wood.rb

# Klasa reprezentujca zbir desek.


class Wood
# Dostp do-odczytu do tablicy boards:
attr_reader :boards
# Konstruktor, moe inicjalizowa tablic boards.
def initialize(*args)
@boards = args
end
# Metoda dodaje pojedyncz deseczk.
def add_board(board)
@boards.push(board)
end
# Metoda zwraca cakowit powierzchni desek.
def total
t = 0
@boards.each { |b| t += (b[:width] *
b[:length]) }
t
end
# Metoda sprawdzajca, czy wystarczy drewna
# dla danego projektu
def enough?(project)
project.total <= self.total
end
# Operator dodawania:
def +(rh)
@boards += rh.boards
self
end
# Operator przyrwnania:
def ==(rh)
self.total == rh.total
end

Poniewa przykad ten bdzie zawiera dosy


duo kodu, znajdowanie bdw i ich poprawianie
bdzie bardzo kopotliwe, jeli uywasz powoki
irb lub fxri. Dlatego te zdecydowaem si napisa
go jako skrypt w osobnym pliku. Po wicej
informacji, jak pisa i uruchamia skrypty
w jzyku Ruby, odsyam do rozdziau 2.,
Proste skrypty.
2. Rozpocznij definicje klasy Wood:
class Wood
attr_reader :boards
def initialize(*args)
@boards = args
end

Najpierw metoda getter jest definiowana dla


zmiennej @boards. Nastpnie definiowana jest
metoda initialize w taki sposb, aby moga
przyjmowa zmienn liczb argumentw pod
postaci tablicy (o operatorze splat moesz si
dowiedzie wicej w rozdziale 6., Tworzenie
metod). Zmienna @boards bdzie tablic
haszw, po jednym dla kadej deseczki. Hasze
bd uywa symboli jako kluczy. Aby utworzy
obiekt zawierajcy dwie deseczki, napiszesz:
walnut = Wood.new({:width = > 5, :length = >
26.25}, {:width = > 4.75, :length = > 32})

Jeli podczas tworzenia nowego obiektu nie


bd przekazane adne dane, wwczas @boards
bdzie pust tablic (poniewa args bdzie pust
tablic).

# Operator identyczno:
def ===(rh)
@boards == rh.boards
end

169

Definiowanie operatorw

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# Skrypt 7.1 - wood.rb

Aby zdefiniowa operatory dla klas:

Rozdzia 7.
3. Zdefiniuj metod add_board:
def add_board(board)
@boards.push(board)
end

Jest to prosta metoda suca do dodawania


deseczek do kolekcji poprzez wstawianie
przekazanego argumentu do tablicy.
4. Zdefiniuj metod total:
def total
t = 0
@boards.each { |b| t +=(b[:width] *
b[:length]) }
t
end

Definiowanie operatorw

Metoda total zwraca cakowit powierzchni


reprezentowan przez obiekt. Aby to zrobi,
inicjalizuje zmienn liczb 0, a nastpnie iteruje
po elementach tablicy @boards. Powizany
z iteratorem blok kodu bdzie otrzymywa jedn
deseczk (zmienn typu Hash). Nastpnie doda
do zmiennej t wynik mnoenia szerokoci
i dugoci deseczki. Na koniec zwracana jest
suma.
5. Zdefiniuj metod enough?:
def enough?(project)
project.total < = self.total
end

Metoda ta zwraca informacj, czy posiadany


zapas drewna jest wystarczajcy dla danego
projektu. Aby to zrobi, zwraca true, jeli
powierzchnia potrzebna dla danego projektu
jest mniejsza lub rwna cakowitej powierzchni
desek w obiekcie. Metoda bdzie uywana
w nastpujcy sposb (maple i table s obiektami
typu Wood):
puts "Wybierz inny rodzaj drewna!" if
!maple.enough?(table)

170

Listing 7.1. Klasa Wood umoliwiajca dodawanie


i porwnywanie obiektw typu Wood cig dalszy
48
# Oglny operator porwnania:
49
def <=>(rh)
50
if self.total < rh.total then -1
51
elsif self.total > rh.total then 1
52
else 0 end
53
end
54
55 end # Koniec definicji klasy Wood.
56
57 # Utwrz nowy obiekt klasy Wood:
58 maple = Wood.new({:width => 7, :length =>
42}, {:width => 4.5, :length => 22},
{:width => 6.75, :length => 32.5},
{:width => 5, :length => 26.25})
59
60 # Utwrz drugi obiekt klasy Wood:
61 oak = Wood.new
62 oak.add_board({:width => 5, :length =>
26.25})
63 oak.add_board({:width => 6.25, :length =>
27.5})
64
65 # Kilka testw:
66 print "Cakowita powierzchnia desek
z klonu: "
67 puts maple.total
68 print "Cakowita powierzchnia desek
dbowych: "
69 puts oak.total
70 print "Czy cakowite powierzchnie desek
z klonu i dbowych s rwne? "
71 puts maple == oak
72 print "Porwnanie cakowitych powierzchni
desek z klonu i dbowych: "
73 puts maple <=> oak
74
75 # Kup wicej desek dbowych:
76 oak += Wood.new({:width => 7, :length =>
19}, {:width => 5.5, :length => 22.2})
77
78 # Sprawd, czy wystarczy dla projektu:
79 table = Wood.new({:width => 4.5, :length
=> 22}, {:width => 6.75, :length =>
32.5}, {:width => 5, :length => 26.25},
{:width => 5, :length => 26.25})
80 print "Wymagana powierzchnia dla stou: "
81 puts table.total
82 print "Czy wystarczy desek z klonu? "
83 puts maple.enough?(table) ? "Do pracy!":
"Niestety!"
84 print "Czy wystarczy desek dbowych? "
85 puts oak.enough?(table) ? "Do pracy!":
"Niestety!"

Klasy
6. Zdefiniuj operator dodawania:
def +(rh)
@boards += rh.boards
self
end

Obiekty zmieniajce same siebie


Niektre metody klas modyfikuj obiekty,
do ktrych nale. Przyja si konwencja,
e ich nazwa koczy si wykrzyknikiem,
zaznaczajc, e naley ich uywa
z ostronoci (na przykad metoda slice!
klasy Array). Metoda moe aktualizowa
obiekt poprzez zmian wartoci zmiennych
instancji.
def double!
@some_var *= 2
end

Wan spraw, na ktr naley zwrci


uwag, jest warto, ktr zwraca metoda.
Poprzednia metoda zwraca warto zmiennej
@some_var pomnoon razy 2. Wszystko
jest w porzdku, dopki obiekt nie zostanie
wykorzystany w taki sposb:

W tym momencie zmienna my_obj nie jest


ju instancj klasy SomeClassName, zamiast
tego posiada warto @some_var razy 2.
Rozwizaniem tego problemu jest zwracanie
przez metod biecego obiektu,
reprezentowanego przez self:
def double!
@some_var *= 2
self
end

Jeli potrzebujesz metody speniajcej to samo


zadanie, ale bez modyfikowania biecego
obiektu (tak jak metoda slice klasy Array),
niech najpierw utworzy kopi biecego
obiektu, a nastpnie wywoaj metod, ktra
zmienia biecy obiekt:

my_oak += your_oak

Uycie operatora + dodaje tablic @boards,


ktra jest prawym operandem do tablicy @boards
biecego obiektu. Metoda zwraca biecy
obiekt (self).
7. Zdefiniuj operatory przyrwnania i identycznoci:
def ==(rh)
self.total == rh.total
end
def ===(rh)
@boards == rh.boards
end

Aby zademonstrowa, w jaki sposb mona to


zrobi, nawet jeli w rzeczywistoci nigdy nie
zostan uyte, operatorom == (przyrwnanie)
i === (identyczno) przypisano funkcje. Obiekty
bd rwne, gdy dwie kolekcje bd miay tak
sama powierzchni, nawet jeli bd miay
rn liczb deseczek. Identyczno jest bardziej
restrykcyjn wersj przyrwnania. Tutaj
zdefiniowano j jako sprawdzenie, czy dwa
obiekty posiadaj tablice @boards z takimi
samymi elementami i w tej samej kolejnoci.
8. Zdefiniuj oglny operator porwnania:
def <=>(rh)
if self.total < rh.total then -1
elsif self.total > rh.total then 1
else 0 end
end

def double
copy = self.dup
copy.double!
end

171

Definiowanie operatorw

my_obj = SomeClassName.new
my_obj = my_obj.double!

Jeli posiadasz zapas drewna i kto sprezentuje


Ci swj, oba zapasy bd do siebie dodane:

Rozdzia 7.
Jeli lewy operand (biecy obiekt) ma mniejsz
powierzchni ni prawy operand, zwraca si -1.
Jeeli posiada wiksz, wwczas zwraca si 1.
Jeli oba maj tak sam powierzchni, zwraca
si 0 i przyjmuje si, e obiekty s rwne.
9. Zakocz definiowanie klasy i utwrz kilka

obiektw tego typu:


end
maple = Wood.new({:width = > 7, :length = > 42},
{:width = > 4.5, :length = > 22}, {:width = >
6.75, :length = > 32.5}, {:width = > 5,
:length = > 26.25})
oak = Wood.new
oak.add_board({:width = > 5, :length = > 26.25})
oak.add_board({:width = > 6.25, :length = >
27.5})

Najpierw utwrz nowy obiekt typu Wood o nazwie


maple (klon). Podczas jego tworzenia przypisuje
si mu deseczki. Nastpnie tworz nowy obiekt
typu Wood o nazwie oak (db). Na pocztku nie
posiada on desek, wic dwie zostaj dooone.
10. Pobaw si z obiektami:

Definiowanie operatorw

print "Cakowita powierzchnia desek z klonu: "


puts maple.total
print "Cakowita powierzchnia desek dbowych: "
puts oak.total
print "Czy cakowite powierzchnie desek z klonu
i dbowych s rwne? "
puts maple == oak
print "Porwnanie cakowitych powierzchni
desek z klonu i dbowych: "
puts maple <=> oak

Aby zobaczy, co te obiekty potrafi zrobi,


wywoujemy metod total oraz wykorzystujemy
dziaanie dwch operatorw.

172

Klasy
11. Do kilka desek dbowych:
oak += Wood.new({:width => 7, :length => 19},
{:width => 5.5, :length => 22.2})

W ten sposb sprawdzamy dziaanie operatora


+ dla obiektw klasy Wood.
12. Sprawd, czy wystarczy drewna klonowego

lub dbowego dla danego projektu:


table = Wood.new({:width => 4.5, :length => 22},
{:width => 6.75, :length => 32.5}, {:width => 5,
:length => 26.25}, {:width => 5, :length =>
26.25})
print "Wymagana powierzchnia dla stou: "
puts table.total
print "Czy wystarczy desek z klonu? "
puts maple.enough?(table) ? "Do pracy!":
"Niestety!"
print "Czy wystarczy desek dbowych? "
puts oak.enough?(table) ? "Do pracy!":
"Niestety!"

13. Zapisz i uruchom skrypt (rysunek 7.18).

Rysunek 7.18. Wynik wykonania skryptu wood.rb

173

Definiowanie operatorw

Tworzymy nowy obiekt typu Wood o nazwie


table (st). Symbolizuje on hipotetyczny st,
ktry chcemy zbi z desek. Majc ten obiekt,
moemy sprawdzi, czy wystarczy desek dla
naszego projektu, przez porwnanie go z innymi
obiektami klasy Wood.

Rozdzia 7.
Wskazwki
Jeli potrzebujesz zdefiniowa

jednoargumentowy minus lub plus, nazwij


metody w nastpujcy sposb: -@, +@.
Nie moesz po prostu uy - i +, poniewa
reprezentuj one odejmowanie i dodawanie.
Definicj klasy zazwyczaj umieszcza si

w osobnym pliku, a nastpnie docza


w skryptach, ktre uywaj danej klasy.
W rozdziale 9., Moduy, zobaczysz,
jak to zrobi.
Aby umoliwi traktowanie obiektu jak tablicy,
naley zdefiniowa metod []. Na przykad

nastpna metoda umoliwi dostp do atrybutu


@height klasy Rectangle w nastpujcy sposb:
nazwa_obiektu[0] lub nazwa_obiektu[-2],
a do atrybutu @width w nastpujcy:
nazwa_obiektu[1] lub nazwa_obiektu[-1]:

Definiowanie operatorw

def [](i)
case i
when 0, -2: @height
when 1, -1: @width
else nil
end
end

Jeli chcesz, aby obiekt mg by taktowany


jako hasz, moesz doda klauzule when dla
:height i 'height' oraz :width i 'width'.

174

Klasy

Metody specjalne

Dla klasy Rectangle moesz zwrci wartoci


umieszczone w kontekcie (rysunek 7.19):

Z wielkimi moliwociami definiowania


class Rectangle
i korzystania z wasnych klas wi si obowizki
attr_reader :height, :width
waciwego ich definiowania. Jednym z tych
def initialize(h, w)
@height, @width = h, w
obowizkw jest takie ich definiowanie, aby
end
zachowyway si tak jak inne obiekty jzyka
def to_s
Ruby (o ile to moliwe). Na przykad wikszo
"wysoko: #@height, szeroko: #@width"
klas posiada metod to_s, ktra zwraca tekstow
end
# pozostae metody
reprezentacj obiektu. Dla typw liczbowych
end
to_s zwraca warto jako cig znakw. Dla tablic
to_s jest rwnowana uyciu join.
W niektrych klasach sensowne jest rwnie
Aby utworzy wasn metod to_s, po prostu zdefiniowanie metody each, aby umoliwi iteracj.
zdefiniuj metod o takiej nazwie. Mona si Definiowanie tej metody nie zawsze jest celowe,
ale moe by przydatne, jeli klasa przechowuje
spodziewa, e metoda taka moe zwraca
wiele
wartoci. Zostanie to pokazane w nastpnym
wartoci zmiennych klas:
przykadzie klasie zarzdzajcej listami zakupw.
class Dog
Lista przechowywana jest w haszu, ktrego klucze
def initialize(name)
s nazwami produktw, a wartoci liczb produktw.
@name = name
Metoda initialize tworzy nowy hasz. Metoda add
end
def to_s
dodaje element do listy. W klasie zaimplementowano
@name
rwnie metody each oraz to_s, dlatego w miar
end
potrzeby
obiekt zawierajcy list zakupw bdzie
end
mg by traktowany jako hasz.

Metody specjalne

Rysunek 7.19. Przykad dziaania metody to_s dla obiektw


typu Rectangle

175

Rozdzia 7.
Aby utworzy metody to_s i each:
1. Utwrz nowy skrypt w edytorze lub IDE

(listing 7.2):
# Skrypt 7.2 - groceries.rb

Tak jak poprzedni, ten przykad rwnie


bdzie napisany jako skrypt.
2. Rozpocznij definicj klasy GroceryList:
class GroceryList
def initialize
@items = Hash.new
end

Metoda initialize nie przyjmuje adnych


argumentw, tworzy pusty obiekt typu Hash.
Zauwa, e w klasie nie zdefiniowano adnych
akcesorw. Wartoci bd dodawane za pomoc
metody add, list moesz przeglda przy
uyciu each.
3. Zdefiniuj metod add:
def add(item, qty = 1)
@items[item] = qty
end

Metody specjalne

Metody tej bdziemy uywa do dodawania


produktw do listy. Przyjmuje dwa argumenty:
nazw produktu i liczb. Dziki ustawieniu
domylnej liczby na 1 podanie jej jest opcjonalne.
Wewntrz tej metody nowe produkty s
dodawane poprzez dodanie nowego elementu
do hasza. Nazwa produktu jest kluczem, a liczba
produktw stanowi warto elementu hasza.
Jeli dany element istnieje, liczba zostanie
nadpisana.
4. Zdefiniuj metod each:
def each
@items.each_pair { |k, v| yield k, v }
end

176

Listing 7.2. Udostpniajc metody each oraz to_s,


klasa GroceryList zachowuje si podobnie
do standardowych klas w jzyku Ruby
1 # Skrypt 7.2 - groceries.rb
2
3 # Definicja nowej klasy:
4 class GroceryList
5
6
# Konstruktor tworzy pusty hasz.
7
def initialize
8
@items = Hash.new
9
end
10
11
# Metoda dodajca elementy do listy.
12
# Liczba jest opcjonalna, domylnie 1.
13
# Nazwa elementu jest uywana jako klucz.
14
def add(item, qty = 1)
15
@items[item] = qty
16
end
17
18
# Metoda suca do iterowania po elementach listy.
19
# Zwraca klucz i warto.
20
def each
21
@items.each_pair { |k, v| yield k, v }
22
end
23
24
def to_s
25
str = ''
26
@items.each_pair { |k, v| str
+= "#{k}: #{v}, "}
27
str.slice(0, str.length - 2).to_s
28
end
29
30 end # Koniec definicji klasy GroceryList.
31
32 # Utwrz now list:
33 g = GroceryList.new
34
35 # Dodaj kilka elementw:
36 g.add('cukier', '5kg')
37 g.add('kiwi', 4)
38 g.add('stek')
39
40 # Wywietl list jako cig znakw:
41 puts "Lista jako cig znakw=> " + g.to_s
42
43 # Wywietl kady element listy w nowym wierszu:
44 puts "Lista zakupw"
45 g.each { |item, qty| puts "#{qty} #{item}" }

Klasy
Metoda each, bdca iteratorem, powinna
porusza si po elementach hasza i zwraca
wartoci do powizanego z ni bloku kodu.
(Iteratory zawsze korzystaj z yield i wywoywane
s z blokiem kodu, patrz rozdziay 5., Struktury
sterujce, i 6., Tworzenie metod).
Do poruszania si po elementach hasza
wykorzystuje si iterator each_pair. W kadym
kroku iteracji klucz i warto hasza zostan
przypisane zmiennym k i v bloku. Nastpnie
zostaj one zwrcone przez iterator (do bloku
kodu, w ktrym zostaa wywoana metoda).
Moe si to wydawa niepotrzebne, ale mona
to wytumaczy w ten sposb: @items jest haszem,
wic moe korzysta z metody each_pair (lub
each) klasy Hash; GroceryList nie jest haszem,
wic musi zdefiniowa wasn metod each
(nawet jeli ta korzysta z innego iteratora).
5. Zdefiniuj metod to_s:
def to_s
str = ''
@items.each_pair { |k, v| str += "#{k}:
#{v}, "}
str.slice(0, str.length - 2).to_s
end

Poniewa metoda slice zwrci nil, jeli cig


znakw bdzie pusty, na koniec wywouje si
metod to_s, ktra w razie potrzeby zamiast
nil zwrci pusty cig znakw.

177

Metody specjalne

Metoda ta zwrci hasz zamieniony w cig


znakw postaci klucz: warto, gdzie kada para
klucz warto bdzie oddzielona przecinkiem
i spacj. Najpierw zostaje zdefiniowany pusty
cig znakw, nastpnie iterator each_pair zwraca
kady element hasza. Wewntrz powizanego
bloku kodu kada para jest doczana do cigu
znakw. Nastpnie cig znakw jest zwracany
bez dwch ostatnich znakw (koczcego
przecinka i spacji).

Rozdzia 7.
6. Zakocz definicj klasy i utwrz obiekt tego typu:
end
g = GroceryList.new

7. Dodaj kilka elementw do listy:


g.add('cukier', '5kg')
g.add('kiwi', 4)
g.add('stek')

Nie zosta zdefiniowany sposb zapisu liczby


produktw, wic moesz uywa liczb oraz
cigw znakw. Poniewa drugi argument
posiada domyln warto 1, jest opcjonalny (jak
pokazano w momencie dodania steku do listy).
8. Wywietl list zakupw w postaci cigu

znakw:
puts "Lista jako cig znakw=> " + g.to_s

Aby to zrobi, moesz skorzysta z metody to_s.


Jako cig znakw moe zosta wywietlona,
zamieniona w tablic, mona j przeszukiwa,
mona z ni zrobi wszystko to, co robi si ze
standardowym obiektem klasy String.
9. Wywietl kady element listy w osobnym

wierszu:

Metody specjalne

puts "Lista zakupw"


g.each { |item, qty| puts "#{qty} #{item}" }

Najpierw wywietlany jest tytu, nastpnie


wywouje si metod each. Zwraca ona kady
produkt i jego liczb. Wartoci te zostan
przypisane zmiennym item i qty w bloku,
a nastpnie wywietlone.
10. Zapisz i uruchom skrypt (rysunek 7.20).

Rysunek 7.20. Wynik wykonania skryptu groceries.rb w edytorze SciTE pod systemem Windows

178

Klasy
Wskazwki
Innym sposobem implementacji metody to_s
jest skorzystanie z metody each klasy:
def to_s
str = ''
self.each { |k, v| str +=
str.slice(0, str.length end

"#{k}: #{v}, " }


2).to_s

Inn metod, ktrej implementacj powiniene


rozway, jest coerce. Metoda ta zamienia

miejscami argumenty w wykonywanym


poleceniu:
def coerce(rh)
[self, rh]
end

Jeli w klasie GroceryList zdefiniujemy metod


odczytujc dla zmiennej @items
attr_reader :items

wwczas nie trzeba bdzie definiowa metody


each wewntrz klasy, poniewa bdzie mona
skorzysta z niej na zewntrz klasy:
g.items.each { |item, qty| puts "#{qty}
#{item}" }

To jednak zakada, e uytkownik wiedzia,


i w klasie istnieje zmienna @items oraz e jest
ona haszem. Najlepiej by byo, gdyby klasy byy
tak tworzone, aby mona byo z nich korzysta
bez znajomoci, a nawet bez dostpu do ich
implementacji. Nazywa si to enkapsulacj;
szerzej opisz to w nastpnym rozdziale.

179

Metody specjalne

Metoda ta jest niezbdna, jeli klasa moe zosta


uyta w operacjach arytmetycznych z inn klas.
Na przykad klasa Wood obsuguje mnoenie,
wic moesz podwoi lub potroi zapas drewna.
W takim przypadku zadziaa maple * 2, ale 2 *
maple ju nie (poniewa klasa Fixnum nie
pozwala na mnoenie jej przez obiekt Wood).
Jeli w klasie Wood zdefiniujemy metod coerce,
bdzie ona moga zamieni miejscami operandy
i wykona mnoenie.

Rozdzia 7.

Walidacja i duck typing


W klasie Wood przedstawionej w skrypcie 7.1
istniej bdy projektowe: zakada si, e jej
metody i operatory bd uywane waciwie.
Metoda enough?, podobnie jak operator
przyrwnania, zakada, e otrzyma obiekt
z metod total. Operatory + i === zakadaj,
e bd wykorzystywane jedynie z operandami
posiadajcymi zmienn @boards. Kade
z poniszych wywoa spowoduje
wystpienie bdw (rysunek 7.21):
puts !maple.enough?(439.50)
oak + = 35

Walidacja i duck typing

Aby zapobiec niewaciwemu uyciu metod,


moesz doda kod walidujcy, ktry przerwie
wykonywanie metody, jeli nie bd spenione
okrelone warunki. Jednym z rodzajw walidacji
jest sprawdzenie typu uywanych obiektw.
Metoda instance_of? zwraca warto logiczn,
ktra wskazuje, czy jest to obiekt ustalonej klasy:
class ThisClass
def -(rh)
return nil unless rh.instance_of?
ThisClass
# W porzdku, wykonaj odejmowanie.
end
end

Metoda bdzie zwraca nil, jeli prawy operand


nie bdzie typu ThisClass.
Jeli chcesz by bardziej elastyczny, moesz
sprawdza, czy obiekt nie jest obiektem danego
typu lub typu pochodnego. Dziedziczenie jest
dokadnie opisane w nastpnym rozdziale, na
razie wiedz, e umoliwia ono pochodzenie jednej
klasy od innej.
class Rectangle
def <=>(rh)
return nil unless rh.is_a? Shape
self.area <=> rh.area
end
end

W powyszym przykadzie zakada si, e mona


porwnywa obiekty klasy Rectangle z obiektami
klasy Shape oraz jej klas pochodnych. Moe to
dotyczy klas Rectangle, Circle oraz Triangle.
W takich przypadkach mona dokona porwnania,
poniewa kady obiekt klasy pochodzcej od Shape
powinien posiada metod area (porwnanie
sugeruje, e prostokt jest wikszy od koa, jeli
jego pole jest wiksze od pola koa). Jednake jeli
zaley Ci jedynie na tym, aby obiekt posiada dan
metod, to istnieje jeszcze bardziej liberalny sposb
walidacji.

Rysunek 7.21. Rne rodzaje bdw, ktre mog wystpi, jeli metody
i operatory bd wywoywane z niewaciwymi typami lub operandami

180

Klasy
Na przykad zamy, e chcesz stworzy klas,
ktra bdzie generowaa tablice w jzyku HTML.
Klasa taka moe zamienia tablic na tablic
w HTML-u. Ale dlaczego nie mogaby zamienia
hasza albo zakresu? Tak naprawd dlaczego nie
mogaby zamienia obiektu typu GroceryList,
ktry rwnie jest list wartoci? To, czego taka
klasa naprawd wymaga, moe by dowolnym
obiektem posiadajcym iterator each. Aby sprawdzi
taki warunek, uyj metody respond_to?:
class HtmlTable
# Kod klasy.
def make_rows(data)
return nil unless data.respond_to?('each')
data.each { # blok kodu
end
end

Wsparcie dla duck typing, a nawet preferowanie


takiego tworzenia kodu jest jedn z wielu cech,
ktre odrniaj Ruby od innych jzykw
programowania. Odzwierciedla to cz filozofii
jzyka Ruby: to, co obiekt moe zrobi, jest
waniejsze od tego, czym jest.
Aby wyprbowa to podejcie, stwrzmy nowe
wersje klas Rectangle oraz Circle i zobaczmy,
co mona z nimi zrobi.

181

Walidacja i duck typing

Taki sposb tworzenia kodu, w ktrym wiksz


wag przywizuje si do tego, co obiekt moe
robi, ni do tego, jakiego jest typu, znany jest
pod nazwa kacze typowanie (duck typing). Nazwa
pochodzi od powiedzenia: Jeli chodzi jak kaczka
i kwacze jak kaczka, nazwabym to kaczk (If it
walks like a duck and quacks like a duck, I would call
it a duck sowa przypisywane amerykaskiemu
poecie i pisarzowi, Jamesowi Whitcombowi
Rileyemu). Gdy stosuje si takie podejcie przy
tworzeniu klasy HtmlTable, korzyci jest to, i
dziaaaby z obiektem dowolnego typu, byle tylko
posiada implementacj metody each. Dotyczy to
rwnie klas utworzonych dawno po napisaniu
klasy HtmlTable.

Rozdzia 7.
Aby wykorzysta duck typing:
1. Utwrz nowy skrypt w edytorze lub IDE

(listing 7.3):
# Skrypt 7.3 - ducks.rb

2. Rozpocznij definiowanie klasy Rectangle:


class Rectangle
attr_reader :height, :width
def initialize(h, w)
@height, @width = h, w
end
def area
@height * @width
end

Wikszo definicji klasy pochodzi z poprzednich


przykadw. Aby zaoszczdzi miejsce, nie bd
implementowa metod perimeter i is_square?.
3. Zdefiniuj metod +:
def +(rh)
return nil unless rh.instance_of? Rectangle
@height += rh.height
@width += rh.width
self
end

Walidacja i duck typing

Dodawanie bdzie wykonywane jedynie dla


dwch obiektw typu Rectangle. Wewntrz tej
metody pierwszy wiersz zwraca nil, co powoduje
wyjcie z metody, jeli otrzymany argument
prawy operand nie jest obiektem typu
Rectangle. Jeli warunek rh.instance_of?
zwrci true, nastpi odpowiednio dodawanie
obu wysokoci i obu szerokoci. Nastpnie
zostanie zwrcony biecy obiekt.

182

Listing 7.3. Skrypt, ktry korzysta z metod


instance_of? oraz responds_to? w celu kontroli,
jakie typy obiektw mog by ze sob uywane
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# Skrypt 7.3 - ducks.rb


# Bardzo prosta definicja klasy Rectangle:
class Rectangle
attr_reader :height, :width
def initialize(h, w)
@height, @width = h, w
end
def area
@height * @width
end
# + powinien dziaa jedynie z innymi
# obiektami Rectangle!
def +(rh)
return nil unless rh.instance_of?
Rectangle
@height += rh.height
@width += rh.width
self
end
# Porwnywa mona z kadym obiektem, ktry
# posiada metod area.
def <=>(rh)
return nil unless
rh.respond_to?('area')
if (self.area < rh.area) then -1
elsif (self.area > rh.area) then 1
else 0 end
end
end # Koniec definicji klasy Rectangle.
# Prosta definicja klasy Circle:
class Circle
attr_reader :radius
def initialize(r)
@radius = r
end
def area
Math::PI * @radius**2
end
end # Koniec definicji klasy Circle.

Klasy
Listing 7.3. Skrypt, ktry korzysta z metod
instance_of? oraz responds_to? w celu kontroli,
jakie typy obiektw mog by ze sob uywane
cig dalszy
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# Utwrz obiekty:
r = Rectangle.new(32, 56)
c = Circle.new(8.4)
# Wywietl informacj o nich:
puts "Pole r wynosi #{r.area}. Pole c
wynosi #{c.area}."
# Wywietl wynik dodawania:
print "Wynik dodawania r + c: "
puts r + c
# Wywietl wynik porwnania:
print "Porwnanie r z c: "
puts r <=> c

4. Zdefiniuj metod <=> i zakocz definicj klasy:


def <=>(rh)
return nil unless rh.respond_to?('area')
if (self.area < rh.area) then -1
elsif (self.area > rh.area) then 1
else 0 end
end
end

Oglny operator porwnania korzysta z kaczego


typowanie do porwnywania obiektw klasy
Rectangle z dowolnymi obiektami posiadajcymi
metod area. Pierwsza linia metody koczy jej
dziaanie, jeli nie jest speniony warunek.
Nastpnie, w zalenoci od wyniku porwnania,
zwracana jest warto -1, 1 lub 0.
5. Zdefiniuj klas Circle:
class Circle
attr_reader :radius
def initialize(r)
@radius = r
end
def area
Math::PI * @radius**2
end
end

6. Utwrz obiekty obu klas:


r = Rectangle.new(32, 56)
c = Circle.new(8.4)

7. Wywietl pole obu figur:


puts "Pole r wynosi #{r.area}. Pole c wynosi
#{c.area}."

183

Walidacja i duck typing

Jest to podstawowa wersja klasy Circle,


zawierajca jedynie metody initialize i area.
Jeli chcesz, moesz rwnie zdefiniowa
operator <=>, dokadnie tak, jak jest to zrobione
w klasie Rectangle, w ten sposb obiekty
typu Circle mog by lewymi operandami
porwnania.

Rozdzia 7.
8. Wywietl wynik dodawania obu figur:
print "Wynik dodawania r + c: "
puts r + c

Poniewa klasa Rectangle wymaga, aby prawy


operand rwnie by typu Rectangle, wynikiem
dodawania bdzie nil (warto zwrcona przez
metod).
9. Wywietl wynik porwnania obu figur:
print "Porwnanie r z c: "
puts r <=> c

Dziki korzystaniu z kaczego typowania mona


porwna obie figury.
10. Zapisz i uruchom skrypt (rysunek 7.22).

Wskazwki

Walidacja i duck typing

Innym podejciem do definiowania metod

w klasach jest przyjmowanie, e wszystkie dane


otrzymane z wejcia s poprawne (to znaczy
zakadanie, e wszystko na wejciu jest kaczk),
a nastpnie zgaszanie wyjtkw w przypadku
niepoprawnych danych wejciowych. Podejcie
to jest opisane w rozdziale 11., Debuggowanie
i obsuga bdw.
Poniewa w klasie Circle nie zdefiniowalimy
operatora <=>, jeli sprbujemy takiego

porwnania, wystpi bd:


c <=> r

Rozwizaniem bdzie zdefiniowanie operatora


<=> w klasie Circle lub metody coerce w klasie
Rectangle.

Rysunek 7.22. Poprzez obserwacj typw uywanych obiektw


skrypty mog by bardziej lub mniej restrykcyjne, jeli chodzi
o dozwolone operacje na obiektach

184

You might also like