Ruby/Kontrola dostępu

Kontrola dostępu

edytuj

Wcześniej powiedzieliśmy, że Ruby nie posiada funkcji, tylko metody. Jednakże jest nieco więcej różnych rodzajów metod. W tym rozdziale przedstawimy sposoby kontroli dostępu.

Rozważmy, co się stanie, gdy zdefiniujemy metodę na samym szczycie hierarchii, nie wewnątrz jakiejkolwiek klasy? Możemy myśleć o takiej metodzie analogicznie jak o funkcji w bardziej tradycyjnym języku, takim jak C.

def kwadrat(n)
  n * n
end

kwadrat(5) #=> 25

Wydaje się, że nasza nowa metoda nie należy do żadnej klasy, ale w rzeczywistości Ruby dodał ją do klasy Object która jest nadklasą każdej innej klasy. W rezultacie każdy obiekt powinien mieć możliwość używania tej metody. Jest to prawdą, ale z małym kruczkiem: jest to prywatna metoda każdej klasy. Wprawdzie będziemy jeszcze dokładnie rozważać co to znaczy, ale zauważmy, że jedną z konsekwencji tego faktu jest to, że metoda ta może być wywołana tylko w funkcyjnym stylu, jak poniżej:

class Klasa
  def czwarta_potega_z(x)
    kwadrat(x) * kwadrat(x)
  end
end

Klasa.new.czwarta_potega_z(10) #=> 10000

Wyraźnie nie możemy wywołać metody na rzecz obiektu:

"ryba".kwadrat(5)
ERR: (eval):1: private method `kwadrat' called for "ryba":String

To raczej zręcznie chroni czysto obiektową naturę Rubiego (funkcje są wciąż metodami obiektów, ale odbiorcą domyślnie jest self) dostarczając funkcji które mogą być zapisane podobnie jak w bardziej tradycyjnym języku.

Powszechną dyscypliną umysłową w programowaniu obiektowym, którą zasugerowaliśmy we wcześniejszym rozdziale, jest problem rozdzielenia specyfikacji i implementacji, czyli jakie zadania wymagamy by obiekt wypełniał i jak je właściwie wypełnia. Wewnętrzne prace obiektu powinny być zazwyczaj ukryte przed jego użytkownikami. Powinni oni dbać o to, co wchodzi i wychodzi do/z obiektu oraz ufać, że obiekt wie co robi wewnętrznie z danymi. Z tego powodu często pomocne jest, gdy klasa posiada metody niewidoczne z zewnątrz, ale używane wewnętrznie, które mogą być poprawione przez programistę kiedy tylko zajdzie taka potrzeba, bez zmieniania sposobu, w jaki użytkownicy widzą obiekty danej klasy. W trywialnym przykładzie poniżej, pomyśl o metodzie eval jako o niewidocznych pracach klasy.

class Test
  def dwa_razy(a)
    puts "#{a} razy dwa to #{wylicz(a)}"
  end
  
  def wylicz(b)
    b*2
  end
  
  private :wylicz  # to ukryje wylicz przed uzytkownikami
end

test = Test.new
test.wylicz(6)
ERR: (eval):1: private method `wylicz' called for #<Test:0x4017181c>
test.dwa_razy(6)
#=> 6 razy dwa to 12

Moglibyśmy oczekiwać, że test.wylicz(6) zwróci 12, ale zamiast tego nauczyliśmy się, że metoda wylicz jest niedostępna, gdy odgrywamy rolę użytkownika obiektu Test. Tylko inne metody klasy Test, takie jak dwa_razy mogą korzystać z wylicz. Od nas wymagane jest posługiwanie się publicznym interfejsem, który składa się z metody dwa_razy. Programista, który ma pod kontrolą tę klasę, może swobodnie modyfikować wylicz (tutaj, być może zmieniając b*2 na b+b i argumentując to przypuszczalnie wzrostem wydajności) bez wpływania na to jak użytkownik współdziała z obiektami klasy Test. Ten przykład jest oczywiście zbyt prosty by był użyteczny; korzyści z metod kontroli dostępu staną się bardziej widoczne tylko wtedy, gdy zaczniemy tworzyć bardziej skomplikowane i interesujące klasy.

Modyfikatory dostępu

edytuj

W Rubim mamy dostępne trzy modyfikatory dostępu.

Modyfikator Tłumaczenie Działanie
private prywatny Metoda dostępna tylko dla obiektu danej klasy. Każdy obiekt danej klasy może wywoływać metody prywatne tylko na rzecz samego siebie. W innych językach programowania (np. w Javie) obiekty tej samej klasy mogą wykonywać swoje metody prywatne.
protected chroniony Metoda dostępna dla wszystkich obiektów danej klasy (lub klas potomnych).
public publiczny Metoda dostępna dla wszystkich obiektów.

W Rubim modyfikatory dostępu dotyczą tylko metod. Jeżeli nie stosujemy żadnych modyfikatorów dostępu, domyślnym modyfikatorem jest public, tak więc wszystkie metody (poza initialize) są domyślnie publiczne. Dostęp do metod można określać na dwa sposoby:

  1. Stosując słowa kluczowe przed definicjami metod
  2. Stosując symbole o nazwach metod

Modyfikatory przed definicjami metod

edytuj
class Test
  public # każda metoda (poza initialize, która jest prywatna) jest domyślnie publiczna

  def pub_met1
  end

  def pub_met2
  end
  
  private # metody prywatne

  def priv_met1
  end
  
  protected # metody chronione

  def prot_met1
  end

  def prot_met2
  end
end

Jak widzimy, modyfikatory dostępu (private, protected, public) występują tu przed definicjami metod.

Modyfikatory z symbolami

edytuj

Identyczny efekt (z tym, że dostęp będzie nadawany dynamicznie) można uzyskać stosując modyfikatory oraz nazwy metod jako symbole. Co to są symbole powiemy dokładnie w rozdziale o symbolach.

class Test
  def pub_met1
  end

  def pub_met2
  end

  def priv_met1
  end

  def prot_met1
  end

  def prot_met2
  end

  public :pub_met1, :pub_met2
  private :priv_met1
  protected :prot_met1, :prot_met2
end

Nazwy metod po modyfikatorach poprzedzone są dwukropkami (:) - tak właśnie oznaczamy symbole. Ale czym są owe symbole? Dowiedzmy się!