Очень просто программировать несколько лет на Ruby и никогда не обращать внимание на декларирование видимости методов (прав доступа к методам) при помощи public, private и protected. Я так говорю потому, что и сам не обращал на это внимание. Я пришел в Ruby из С/C++, поэтому я отлично понимал концепцию public и private, и я догадывался о том, что собой представляет декларация protected в Ruby, но я никогда не пытался разобраться со всем этим более подробно. В конечном счете по некоторым причинам я заинтересовался этим вопросом и в этой статье я расскажу о различных декларациях видимости методов: public, protected и private.
Примеры
Я не хочу скучно приводить примеры использования. В пояснении разницы между декларациями видимости методов мне помогут самые обыкновенные хоббиты. Они дружественны и открыты для других, но и у них есть также личная жизнь. Многое из их личной жизни доступно для других хоббитов так как в своем хоббитовом царстве они могут бродить по жилищам друг друга. Конечно, каждый хоббит имеет что-то очень личное, такое, что знает один лишь он и никому и никогда не раскроет своей тайны. Такие особенности устройства поведения хоббитов очень похожи на методы в Ruby. Давайте представим каждого хоббита как объект. Кстати, вот и класс для создания хоббитов:
class Hobbit def initialize(name, rooms, has_ring) @name, @rooms, @has_ring = name, rooms, has_ring end def name @name end def name_of(hobbit) hobbit.name end def rooms_of(hobbit) hobbit.rooms end def hobbit_has_ring?(hobbit) hobbit.has_ring? end protected def rooms @rooms end private def has_ring? @has_ring end end
Каждый хоббит имеет 3 атрибута. Имя хоббита (name) декларировано как public, число комнат в доме хоббита (rooms_of) декларировано как protected и наличие кольца (hobbit_has_ring?) — private. name, rooms_of и hobbit_has_ring? — это методы аксессоры для получения значений соответствующих атрибутов объекта хоббита. Теперь давайте создадим пару хоббитов:
irb(main):001:0> require ‘hobbit.rb’
=> true
irb(main):002:0> frodo = Hobbit.new(‘Frodo’, 3, true)
=> #
irb(main):003:0> samwise = Hobbit.new(‘Samwise’, 2, false)
=> #
Публичные (public) методы
В Ruby все методы, для которых явно не декларирована видимость являются public, это означает, что метод доступен ото всюду, из любой части кода, которая знает какому объекту принадлежит метод. Мы можем легко узнать имена каждого хоббита:
irb(main):004:0> frodo.name
=> «Frodo»
irb(main):005:0> samwise.name
=> «Samwise»
Защищенные (protected) методы
Что насчет комнат в их домах? Эта информация доступна только через protected-методы:
irb(main):006:0> frodo.rooms
NoMethodError: protected method `rooms’ called for #
from (irb):6
from :0
irb(main):007:0> frodo.rooms_of(frodo)
=> 3
irb(main):008:0> frodo.rooms_of(samwise)
=> 2
Как вы видите, мы не можем вызвать вызвать protected — метод объекта непосредственно, но один объект может вызвать защищенный метод для себя или любого другого родственного объекта. Таким образом хоббиты скрывают количество комнат в своих домах от других живых существ, но между собой у них в этом секрета нет. Если перейти на чисто программерскую терминологию, то public-методы доступны глобально, а protected могут вызываться только через методы родственных объектов (произошедших от того же класса).
Приватные (private) методы
Что насчет наличия кольца у хоббита? Метод доступа к этой информации помечен как private. Как же мы можем получить к ней доступ?
irb(main):013:0> frodo.has_ring?
NoMethodError: private method `has_ring?’ called for #
from (irb):13
from :0
irb(main):014:0> frodo.hobbit_has_ring?(frodo)
NoMethodError: private method `has_ring?’ called for #
from ./hobbit.rb:19:in `hobbit_has_ring?’
from (irb):14
from :0
irb(main):015:0> frodo.hobbit_has_ring?(samwise)
NoMethodError: private method `has_ring?’ called for #
from ./hobbit.rb:19:in `hobbit_has_ring?’
from (irb):15
from :0
Мы не можем получить доступ к методу has_ring? непосредственно, и не можем получить доступ через другие методы объекта, даже если хоббит спрашивает сам себя. Давайте добавим в класс Hobbit новый метод i_have_the_ring?
class Hobbit def i_have_the_ring? has_ring? end end
Теперь у frodo есть способ проверить есть ли у него кольцо:
irb(main):021:0> frodo.i_have_the_ring?
=> true
В Ruby protected методы остаются открытыми для других объектов того же типа. По этому доступ к protected методу гарантирован пока есть объект — «приемник» (hobbit в hobbit.rooms) произошедший от того же класса что и объект метод которого мы хотим вызвать.
Private — методы позволяют вызывать себя только в контексте собственного объекта. Мы не можем вызвать их как: hobbit.has_ring? или даже так: self.has_ring?, но мы можем воспользоваться private методом просто вызвав его в контексте объекта: has_ring?.
Заключение
public — полностью открытые методы. Различие между protected и private может вызывать проблемы если вы не осведомлены о нем. В Ruby декларация private является даже более строгой, чем private в языке Си. Декларация protected является золотой серединой между public и private позволяя одному хоббиту узнать число комнат в доме другого ;-)
Я использую protected-методы когда они не предназначены для непосредственного вызова и я не пишу для них тестов. Я пишу тесты для public-методов которые вызывают методы обозначенные как protected. Кроме того, я избегаю private-методов потому что я не могу представить потребность блокировать доступ родственных объектов к методам друг-друга (хотя я вероятно должен если я не проверяю их), кроме того из-за использования private методов могут возникать проблемы с дебагом.
Комментарий от переводчика:
Автор поста не призывает вас не использовать private методов, он просто делится своим опытом и утверждает, что просто не видит в ней необходимости а в силу возможных проблем с дебагом старается избегать.
Лично я считаю, что объявление приватных методов необходимо для скрытия внутренней структуры объекта, а также несет в себе так сказать семантическую роль, поскольку дает понять, что методы определенные как private будут использоваться только внутри объекта (программист напрямую их никогда не использует, их используют лишь внутренние методы), то есть стороннему разработчику использующему ваш код не нужно изучать эти методы, кроме тех случаев, когда он хочет переписать ваш класс.
Оригинал на английском здесь: Ruby Method Permissions: The Differences Between Public, Protected, and Private
Лучшая благодарность автору блога — ваши комментарии! Больше комментариев — больше отличных постов ;-)