Ruby/Akcesory: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Szymon wro (dyskusja | edycje)
Szymon wro (dyskusja | edycje)
Nie podano opisu zmian
Linia 3:
=== Czym jest akcesor? ===
 
Krótko omówiliśmy zmienne instancji we [[Ruby/Zmienne instancji|wcześniejszym rozdziale]], ale jeszcze za ;wiele jeszcze z nimi nie robiliśmy. Zmienne instancji obiektu są jego atrybutami, to te rzeczy, które odróżniają obiekt od innych obiektów tej samej klasy. Za ważną czynność zapisywania i odczytywania atrybutów odpowiedzialne są metody nazywane ''akcesorami atrybutów''. Jak za chwilę zobaczymy, nie musimy pisać akcesorów bezpośrednio. Wcześniej jednak poznajmy wszystkie etapy ich tworzenia.
 
Wyróżniamy dwa rodzaje akcesorów: ''piszące'' (ang. ''writer''<ref>W innych językach programowania powszechnie funkcjonuje nazwa ''setter''.</ref>) i ''czytające'' (ang. ''reader''<ref>W innych językach programowania - ''getter''.</ref>).
 
<source lang="ruby">
class Owoc
def zapisz_rodzaj(r) # akcesor piszacy
@rodzaj = r
end
def czytaj_rodzaj # akcesor czytajacy
@rodzaj
end
end
 
o1 = Owoc.new
 
# uzycie akcesora piszacego:
o1.zapisz_rodzaj("brzoskwinia")
 
# uzycie akcesora czytajacego:
o1.czytaj_rodzaj #=> "brzoskwinia"
</source>
 
Gdybyśmy wpisali powyższy kod do ''irba'', moglibyśmy zastosować następujący sposób inspekcji:
 
<pre>
irb(main):011:0> o1
ruby> class Owoc
=> #<Owoc:0x2e47044 @rodzaj="brzoskwinia">
| def zapisz_rodzaj(r) # akcesor piszacy
| @rodzaj = r
| end
| def czytaj_rodzaj # akcesor czytajacy
| @rodzaj
| end
| end
nil
ruby> o1 = Owoc.new
#<Owoc:0xfd7e7c8c>
ruby> o1.zapisz_rodzaj("brzoskwinia") # uzycie piszacego
"brzoskwinia"
ruby> o1.czytaj_rodzaj # uzycie czytajacego
"brzoskwinia"
ruby> o1 # badanie obiektu (inspekcja)
#<Owoc:0xfd7e7c8c @gatunek="brzoskwinia">
</pre>
 
Proste prawda? Możemy przechowywać i odczytywać informację o tym, na jaki owoc patrzymy. Ale nasze nazwy metod są nieco rozwlekłe. W następującym przykładzie są już nieco bardziej zwięzłe i konwencjonalne:
 
<source lang="ruby">
<pre>
ruby> class Owoc
| def rodzaj=(r)
| @rodzaj = r
| end
 
| def rodzaj
def | @rodzaj
| end@rodzaj
| end
end
nil
ruby>
o2 = Owoc.new
o2.rodzaj = "banan"
#<Owoc:0xfd7e7c8c>
ruby> o2.rodzaj #=> "banan"
</source>
"banan"
ruby> o2.rodzaj
"banan"
</pre>
 
=== Metoda <tt>inspect</tt> ===
 
Jest tu potrzebna krótka dygresja. Z pewnością zauważyłeś, że jeżeli próbujemy spojrzeć na obiekt w ''irbie'' bezpośrednio, pokazuje się nam coś zagadkowego w rodzaju <tt>#<jakisObiekt:0x83678></tt>. To jest po prostu domyślne zachowanie i oczywiście możemy je zmienić. Wszystko, co musimy zrobić to dodać metodę o nazwie <tt>inspect</tt>. Powinna ona zwracać łańcuch, który opisuje obiekt w jakiś sensowny sposób, włączając stany części lub wszystkich jego zmiennych instancji.
 
<source lang="ruby">
<pre>
ruby> class Owoc
| def inspect
| "owoc rodzaju: #{@gatunek}"
| end
| end
</source>
nil
ruby> o2
"owoc rodzaju: banan"
</pre>
 
Podobną metodą jest metoda <tt>to_s</tt> (ang. ''convert to string'' - zamień na łańcuch), która jest używana gdy drukujemy obiekt. Ogólnie rzecz biorąc, możesz traktować <tt>inspect</tt> jako narzędzie, gdy piszesz i debugujesz programy, natomiast <tt>to_s</tt> jako sposób na formowanie wyjścia programu. ''eval.rbirb'' używa <tt>inspect</tt> ilekroć wyświetla wyniki. Możesz użyć metody <tt>p</tt> by łatwo debugować wyjście z programów.
 
<pre>
Linia 91 ⟶ 93:
|}
 
Korzystając z tabeli uporządkujmy naszą klasę i dodajmy informację na temat świeżości. Najpierw automatycznie wygenerujemy akcesory: czytającyczytające i piszącypiszące, a następnie dodamy nową informację do metody <tt>inspect</tt>:
 
<source lang="ruby">
<pre>
ruby> class Owoc
| attr_accessor :stan
attr_accessor :gatunek
| def inspect
| "#{@stan} owoc rodzaju: #{@gatunek}"
def | endinspect
"#{@stan} owoc rodzaju: #{@gatunek}"
| end
nilend
end
ruby> o2.stan = "dojrzaly"
 
"dojrzaly"
o = Owoc.new
ruby> o2
o.gatunek = "dojrzaly owoc rodzaju: banan"
o.stan = "dojrzaly"
</pre>
p o #=> dojrzaly owoc rodzaju: banan
</source>
 
=== Więcej zabawy z owocem ===
 
Jeżeli nikt nie zjadł naszego dojrzałego owocu, może należałoby pozwolić, by czas zebrał swoje żniwo. (poniższy kod możemy dopisać bezpośrednio do powyższego):
 
<source lang="ruby">
<pre>
ruby> class Owoc
| def uplywa_czas
| @stan = "gnijacy"
| end
| end
nil
ruby> o2
"dojrzaly banan"
ruby> o2.uplywa_czas
"gnijacy"
ruby> o2
"gnijacy owoc rodzaju: banan"
</pre>
 
p o #=> dojrzaly owoc rodzaju: banan
Ale bawiąc się nieopatrznie wprowadziliśmy mały problem. Co się stanie jeśli spróbujemy teraz utworzyć trzeci owoc. Pamiętaj, że zmienne instancji nie istnieją dopóki nie zostaną im przypisane wartości.
o.uplywa_czas
 
p o #=> gnijacy owoc rodzaju: banan
<pre>
</source>
ruby> o3 = Owoc.new
ERR: failed to convert nil into String
</pre>
 
W [[Ruby/Inicjalizacja obiektów|następnym rozdziale]] zobaczymy, jak zapewnić, by już w momencie utworzenia <tt>Owoc</tt> miał zdefiniowany rodzaj i stan.
To skarży się metoda <tt>inspect</tt> i ma dobry powód. Miała przecież raportować rodzaj owocu oraz jego stan, ale jak dotąd ani jednemu atrybutowi <tt>f3</tt> nie została przypisana żadna wartość. Można by przepisać metodę <tt>inspect</tt>, tak by sprawdzała zmienne instancji używając <tt>defined?</tt> i zwracała wartości tylko wtedy, gdy one istnieją, ale to nie jest zbyt użyteczne. Odkąd każdy owoc ma własny rodzaj i stan, musimy upewniać się, że zawsze mamy je jakoś zdefiniowane. To właśnie będzie tematem następnego rozdziału.
<noinclude>
{{Przypisy}}