Недавно, мы говорили, что Ruby не имеет функций, только методы. Однако имеется более, чем один вид методов. В этой главе мы ознакомимся с контролем доступа.
Рассмотрим, что происходит, когда мы определяем метод на верхнем уровне, не внутри определения класса. Мы можем подумать, что этот метод будет аналогичен функции в более традиционном языке, таком как C.
ruby> def square(n) | n * n | end nil ruby> square(5) 25
Наш новый метод, как кажется, не принадлежит никакому класса, но фактически руби даёт ему класс Object , который является суперклассом для каждого другого класса. В результате, любой объект сможет использовать этот метод. Это справедливо , но имеется небольшая загвоздка: это приватный метод каждого класса. Мы обсудим что это означает ниже, но одним из следствий этого является то, что он может быть вызван только в стиле функции, как здесь:
ruby> class Foo | def fourth_power_of(x) | square(x) * square(x) | end | end nil ruby> Foo.new.fourth_power_of 10 10000
Нам не позволяется выполнять данный метод как метод объекта:
ruby> "fish".square(5) ERR: (eval):1: private method `square' called for "fish":String
Это сохраняет Объектно-ориентированную природу Ruby более чистой (функции всё ещё являются методами объекта, но приёмником неявно является self ) в то время как функции могут быть написаны как в традиционном языке.
Традиционным подходом в объектно-ориентированном программировании, о котором мы намекали в недавних главах, имеется разделение на спецификацию и имплементацию, или какие задачи объекта предполагается выполнять и как они на самом деле выполняются. Внутренняя работа объекта должна сохраняться, как правило, скрытой от пользователей. Они должны заботиться только о том, какие данные выходят и выходят в объект, и верить тому, что объекта знает, что делать с данными внутри себя. Таким образом, часто бывает полезным для классов иметь методы, которые не видны внешнему миру, но которые используются в внутри (и могут быть улучшены программистом, когда он пожелает, без изменения видения пользователями объекта снаружи. В обычном примере, что следует далее, думаем об engine как о невидимом внутреннем работнике класса.
ruby> class Test | def times_two(a) | print a," times two is ",engine(a),"\n" | end | def engine(b) | b*2 | end | private:engine # this hides engine from users | end Test ruby> test = Test.new #<Test:0x4017181c> ruby> test.engine(6) ERR: (eval):1: private method `engine' called for #<Test:0x4017181c> ruby> test.times_two(6) 6 times two is 12. nil
Мы могли бы ожидать выполнения test.engine(6) для возврата значения 12, но вместо этого мы видим, что engine недоступен, когда пользователь применяет его к экземпляру класса Test . Только другим методам класса Test , таким как times_two , позволяется использовать engien . Нам требуется использовать публичный интерфейс, который содержится в методе times_two. Программист, который отвечает за этот класс, может изменить engine (здесь, возможно изменение с b 2 на b+b для улучшения производительности) не затрагивая того, как пользователь взаимодействует с объектами Test.* Этот пример, конечно, очень простой, чтобы быть полезным; преимущества контроля доступа становятся более ясны только тогда, когда мы начинаем создавать более сложные и интересные классы.