Modyfikator Protected w Ruby
💡

Modyfikator Protected w Ruby

Rozmowy rekrutacyjne, a zwłaszcza ich część techniczna najczęściej przebiegają według jakiegoś określonego schematu. Łatwo przewidzieć pytania, jakie mogą paść. Często jest tak, że developer, który je zadaje jest wyrwany z projektu i na co dzień zajęty rozwiązywaniem problemów i nie ma czasu przygotować jakichś super oryginalnych pytań (chyba że jest sadystą i lubi znęcać się na kandydatach, ale kto z nas nie prowadził rekrutacji i nie miał w zanadrzu pytania killera?), więc googluje np. „Ruby interview questions”. Druga możliwość jest taka, że ten developer przeprowadza już n-tą rozmowę, z czystej rutyny zadaje zawsze te same pytania. Może też pytać o to, o co sam został zapytany, gdy to on był kandydatem. Chcę Ci przedstawić i wytłumaczyć kwestię, która na 90% pojawi się na rozmowie rekrutacyjnej dla programisty Ruby on Rails, a chodzi o to czym jest modyfikator Protected w Ruby.

Jaka jest różnica między Public, Private i Protected, czyli modyfikatorami dostępu?

Brzmi znajomo?

Możliwe, że spotykasz się z tymi słowami w innych językach. Aby zrozumieć ich nieco odmienne znaczenie w Ruby przyjrzyjmy się dla porównania jak funkcjonują w Javie, C# i PHP.

Java

Public – dostęp do metody w danym pakiecie jak i w każdym innym

Private – dostęp tylko w obrębie klas i niemożliwe do dziedziczenia

Protected – jest prawie identyczny jak private, z jedną subtelną różnicą, metody protected mogą być dziedziczone

C#

Public – dostęp do metody jest nieograniczony z każdego miejsca w aplikacji

Private – metody dostępne tylko w obrębie klasy lub struktury, w której są zdefiniowane

Protected – metoda jest dostępna w potomnej klasie tylko, jeśli dostęp odbywa się poprzez klasę potomną

PHP

Public – metoda jest widoczna ze środka klasy, klas pochodnych oraz na zewnątrz klasy

Private – metody dostępne tylko i wyłącznie ze środka klasy

Protected – metoda jest widoczna we wszystkich klasach, które dziedziczą po klasie bazowej, włączając w to także klasę bazową

RUBY 💖💋💪

W pierwszej kolejności, gwoli wyjaśnienia, te trzy słowa: public, private, protected, określane potocznie jako modyfikatory dostępu, tak naprawdę nie są słowami kluczowymi, ale… metodami.

Pamiętaj, że w języku Ruby robiący coś i możliwy do wywołania kod określa się właśnie słowem metoda, a definiuje się przez słowa def i end na pozątku i na końcu danej partii kodu.

Public w Ruby

Niczym się nie różni od innych języków. W Ruby wszystkie metody nieokreślone wyraźnie dwoma pozostałymi modyfikatorami są publiczne, z wyjątkiem konstruktora klasy, czyli metody initialize, która zawsze jest prywatna.

Metody publiczne mogą być wywołane przez każdego i wszędzie, nie podlegają tym samym żadnym ograniczeniom. Aby metoda była publiczna nie jest wymagane żadne słowo kluczowe, wystarczy, że będzie zadeklarowana powyżej deklaracji private protected.

Ciekawostka!

Metody publiczne zadeklarowane globalnie poza wszystkimi klasami są tak naprawdę metodami prywatnymi egzemplarza klasy Object, czyli Ruby’owej super ekstra klasy matki.

Private w Ruby

Odbiorcą metody prywatnej zawsze jest self.

Nie można ich wywołać dla żadnej instancji klasy, w której są zdefiniowane, ponieważ działają tylko wewnątrz, w obrębie tej klasy, a to oznacza, że mogą być wywołane jedynie w kontekście danego obiektu.

Można je wywołać tylko w innych metodach egzemplarza tej samej klasy i jej podklas. Metody prywatne w Ruby są dziedziczone.

Przy korzystaniu z metod prywatnych w ciele innych metod należy pamiętać, że zawsze robimy to w stylu funkcyjnym czyli:

metoda_prywatna()

a nie

instancja.metoda_prywatna()

lub

self.metoda_prywatna()

Z tego wynika też, że nie ma dostępu do prywatnych metod innego obiektu nawet, jeśli ten obiekt jest tej samej klasy.

Protected w Ruby

Dostęp do metod protected jest ograniczony tylko do rodziny, ale w porównaniu z innymi językami, mogą być wywoływane przez obiekty tej samej klasy, ale wewnątrz tych obiektów.

Można je wywołać zawsze na rzecz dowolnego egzemplarza klasy lub jej podklas, a więc w przeciwieństwie do private, nie ogranicza się ich tylko do niejawnych wywołań na rzecz self.

Protected stosuje się, aby operować na stanie wewnętrznym innych obiektów tej samej klasy.

Jest to metoda dostępowa pozwalająca egzemplarzowi danej klasy na współdzielenie stanu wewnętrznego, ale jednocześnie zabrania konsumentom tej klasy uzyskać dostęp do tego stanu z zewnątrz.

Cytując Matza, twórcę Ruby’ego:

“Metoda chroniona zdefiniowana w klasie C może zostać wywołana na rzecz obiektu O przez metodę w obiekcie P, jeśli klasy O i P są podklasami klasy C lub są jej równe.”

Pora na przykłady, żeby lepiej to zrozumieć modyfikator Protected w Ruby.

Mamy klasę Employee i dwóch pracowników symbolizujących jej instancje. Załóżmy scenariusz, że szef chce porównać ich pensje, jednocześnie nie ujawniając ich na zewnątrz.

class Employee
  attr_reader :salary
  protected :salary

  def greater_salary_than(other_employee)
    @salary > other_employee.salary
  end
end

first_employee = Employee.new
second_employee = Employee.new
first_employee.grater_salary_than(second_employee)

class Persondef older_than?(other_person)
    age > other_person.age
  end

  protected
  def age@age
  end
end

Dobrze widać tutaj to, o czym pisałem powyżej. Metoda chroniona nie jest dostępna poza kontekstem danego obiektu, ale jest dostępna wewnątrz kontekstu innego obiektu, wtedy i tylko wtedy gdy obiekt ten jest tego samego typu.

image

Dodatkowe informacje o Protected w Ruby i pozostałych modyfikatorach

  • metoda chroniona może być wywołana w kontekście danego obiektu jako metoda() albo self.metoda()
  • modyfikatory dostępu można deklarować na dwa sposoby:
    • wszystkie metody po słowie private będą metodami prywatnymi, chyba, że gdzieś po drodze użyjemy protected. I tutaj analogicznie, wszystkie metody po tej deklaracji będą chronione, chyba, że gdzieś po drodze zastosujemy modyfikator private
    • drugi sposób, to najpierw zadeklarowanie wszystkich metod, a potem ustanowienie ich dostępu poprzez wylistowanie ich nazw w połączeniu z odpowiednim modyfikatorem dostępu
# I sposób
private
def metoda

protected
def metoda# II sposób
def metodaend

protected :metoda
  • wywołanie metody respond_to? dla metody chronionej zwróci false. Dla wyjaśnienia, respond_to? to metoda, która zwraca true, jeśli obiekt implementuje metodę podaną jako argument. Domyślnie nie uwzględnia metod prywatnych. Można je brać pod uwagę przekazując do tej metody drugi argument jako true
  • wróćmy jeszcze na chwilę do prywatnych metod. Wprawdzie pisałem, że nie mamy do nich dostępu, ale jest tu jeden wyjątek. Trik znany tylko tym, którzy nie mówią Rabi. Mianowicie, można się do nich dostać wywołując metodę prywatną za pomocą metody send albo ewaluując blok w kontekście obiektu posługując się metodą instance_eval
p = Page.new
p.send :some_private_method
p.instance_eval(some_private_method)

To jest temat na inny wywód, potraktuj to jako szwajcarską ciekawostkę nie próbuj tego w domu.

BTW, jest jeszcze taka metoda public_send, która działa jak send, przy czym pomija metody prywatne. Łatwo pomylić.

Skoro wiesz jak stosować modyfikator protected w Ruby, zobacz też, o co chodzi z symbolami w Ruby.