Чому у Ruby є як приватні, так і захищені методи?


141

Перш ніж прочитати цю статтю , я подумав, що контроль доступу в Ruby працював так:

  • public- до них можна отримати доступ будь-яким об'єктом (наприклад Obj.new.public_method)
  • protected - доступ до них можна отримати лише з самого об'єкта, а також з будь-яких підкласів
  • private - те саме, що захищено, але метод не існує в підкласах

Однак виявляється, що protectedі privateдіяти так само, за винятком того, що ви не можете викликати privateметоди із явним приймачем (тобто self.protected_methodпрацює, але self.private_methodне працює).

У чому сенс цього? Коли існує сценарій, коли ви не хочете, щоб ваш метод викликався з явним приймачем?


3
Якби всім екземплярам Objectбуло дозволено викликати приватні методи всіх інших екземплярів Object, можна було б сказати такі речі 5.puts("hello world").
sepp2k

Відповіді:


161

protected методи можуть викликати будь-який примірник класу, що визначає, або його підкласи.

privateметоди можуть бути викликані тільки зсередини виклику об'єкта. Ви не можете отримати прямий доступ до приватних методів іншого примірника.

Ось короткий практичний приклад:

def compare_to(x)
 self.some_method <=> x.some_method
end

some_methodне може бути privateтут. Це має бути protectedтому, що він потрібен для підтримки явних приймачів. Вашими типовими внутрішніми помічницькими методами зазвичай можуть бути, privateоскільки їх ніколи не потрібно називати так.

Важливо зазначити, що це відрізняється від способів роботи Java або C ++. privateу Ruby подібний до protectedJava / C ++, оскільки підкласи мають доступ до методу. У Ruby немає можливості обмежити доступ до методу з його підкласів, як ви можете privateв Java.

Видимість у Ruby багато в чому є "рекомендацією", оскільки ви завжди можете отримати доступ до методу, використовуючи send:

irb(main):001:0> class A
irb(main):002:1>   private
irb(main):003:1>   def not_so_private_method
irb(main):004:2>     puts "Hello World"
irb(main):005:2>   end
irb(main):006:1> end
=> nil

irb(main):007:0> foo = A.new
=> #<A:0x31688f>

irb(main):009:0> foo.send :not_so_private_method
Hello World
=> nil

9
Ах, добре, що має набагато більше сенсу. Моє непорозуміння виникло від думки privateпроти protectedтого, чи повинен був би підклас успадкувати метод, але насправді про те, звідки метод можна викликати. Дякую!
Kyle Slattery

3
Також приватні методи за замовчуванням RDoc ігноруються при створенні документації, тоді як захищені. Ви завжди можете використовувати - всі прапор, щоб включити їх.
jasoares

Але якщо ви дійсно хочете, щоб це було приватним, чи не можете ви його перемогти send?
Cyoce

78

Різниця

  • Будь-хто може викликати ваші загальнодоступні методи.
  • Ви можете зателефонувати до захищених методів або інший член вашого класу (або клас нащадка) може зателефонувати захищеним методам ззовні. Ніхто більше не може.
  • Тільки ви можете викликати ваші приватні методи, тому що їх можна викликати лише з неявним приймачем self. Навіть ти не можеш телефонувати self.some_private_method; ви повинні зателефонувати private_methodз selfнеявним
    • iGEL зазначає: "Однак є один виняток. Якщо у вас є приватний метод age =, ви можете (і повинні) викликати його з self, щоб відокремити його від локальних змінних."
    • Оскільки Рубін 2.7self приймач може бути явним, self.some_private_methodне допускаються. (Будь-який інший явний приймач все ще заборонений, навіть якщо значення часу виконання збігається self.)

У Рубі ці відмінності - лише порада одного програміста до іншого. Непублічні методи - це спосіб сказати: "Я залишаю за собою право змінити це, не залежати від цього". Але ви все одно отримаєте гострі ножиці sendі можете зателефонувати будь-якому способу, який вам подобається.

Короткий підручник

# dwarf.rb
class Dwarf
  include Comparable

  def initialize(name, age, beard_strength)
    @name           = name
    @age            = age
    @beard_strength = beard_strength
  end

  attr_reader :name, :age, :beard_strength
  public    :name
  private   :age
  protected :beard_strength

  # Comparable module will use this comparison method for >, <, ==, etc.
  def <=>(other_dwarf)
    # One dwarf is allowed to call this method on another
    beard_strength <=> other_dwarf.beard_strength
  end

  def greet
    "Lo, I am #{name}, and have mined these #{age} years.\
       My beard is #{beard_strength} strong!"
  end

  def blurt
    # Not allowed to do this: private methods can't have an explicit receiver
    "My age is #{self.age}!"
  end
end

require 'irb'; IRB.start

Потім ви можете запустити ruby dwarf.rbі зробити це:

gloin = Dwarf.new('Gloin', 253, 7)
gimli = Dwarf.new('Gimli', 62,  9)

gloin > gimli         # false
gimli > gloin         # true

gimli.name            # 'Gimli'
gimli.age             # NoMethodError: private method `age'
                         called for #<Dwarf:0x007ff552140128>

gimli.beard_strength # NoMethodError: protected method `beard_strength'
                        called for #<Dwarf:0x007ff552140128>

gimli.greet          # "Lo, I am Gimli, and have mined these 62 years.\
                           My beard is 9 strong!"

gimli.blurt          # private method `age' called for #<Dwarf:0x007ff552140128>

8
Приємне пояснення! Однак є один виняток. Якщо у вас є приватний метод age=, ви можете (і доведеться) викликати його, selfщоб відокремити його від локальних змінних.
iGEL

Якщо ви зробили "привітання" захищеним методом, чому ви не можете зробити gimli.greet? Оскільки Гімлі є членом Карликового класу, чи не може він без методу викликати цей метод?
JoeyC

@JoeyC, тому що, коли ти це робиш gimli.greet, gimliне дзвонить, а отримувач. Абонент - це "середовище виконання верхнього рівня", яке фактично є спеціальним екземпляром Object. Спробуйте це:ruby -e 'p self; p self.class'
Келвін

52

Приватні методи в Ruby:

Якщо метод є приватним у Ruby, його не можна викликати явним приймачем (об'єктом). Викликати його можна лише неявно. Його можна назвати неявно класом, в якому він описаний, а також підкласами цього класу.

Наступні приклади проілюструють це краще:

1) Клас Animal з приватним методом class_name

class Animal
  def intro_animal
    class_name
  end
  private
  def class_name
    "I am a #{self.class}"
  end
end

В цьому випадку:

n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>error: private method `class_name' called

2) Підклас тварини під назвою Земноводний:

class Amphibian < Animal
  def intro_amphibian
    class_name
  end 
end 

В цьому випадку:

  n= Amphibian.new
  n.intro_amphibian #=>I am a Amphibian
  n.class_name #=>error: private method `class_name' called

Як бачите, приватні методи можна викликати лише неявно. Їх не можна викликати явними приймачами. З цієї ж причини приватні методи не можна викликати поза ієрархією визначального класу.

Захищені методи в Ruby:

Якщо метод захищений у Ruby, то його можна неявно викликати як визначаючим класом, так і його підкласами. Крім того, їх також можна викликати явним приймачем до тих пір, поки одержувач самостійно або того ж класу, що і сам:

1) Клас тварин із захищеним методом protection_me

class Animal
  def animal_call
    protect_me
  end
  protected
  def protect_me
    p "protect_me called from #{self.class}"
  end  
end

В цьому випадку:

n= Animal.new
n.animal_call #=> protect_me called from Animal
n.protect_me #=>error: protected method `protect_me' called

2) Клас ссавців, який успадковується від класу тварин

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

В цьому випадку

n= Mammal.new
n.mammal_call #=> protect_me called from Mammal

3) Клас земноводних, успадкований від класу Animal (те саме, що і клас ссавців)

class Amphibian < Animal
  def amphi_call
    Mammal.new.protect_me #Receiver same as self
    self.protect_me  #Receiver is self
  end   
end

В цьому випадку

n= Amphibian.new
n.amphi_call #=> protect_me called from Mammal
             #=> protect_me called from Amphibian  

4) Клас під назвою Дерево

class Tree
  def tree_call
    Mammal.new.protect_me #Receiver is not same as self
  end
end

В цьому випадку:

n= Tree.new
n.tree_call #=>error: protected method `protect_me' called for #<Mammal:0x13410c0>

7

Розглянемо приватний метод на Java. Звичайно, його можна викликати з одного класу, але він може бути викликаний і іншим екземпляром цього ж класу:

public class Foo {

   private void myPrivateMethod() {
     //stuff
   }

   private void anotherMethod() {
       myPrivateMethod(); //calls on self, no explicit receiver
       Foo foo = new Foo();
       foo.myPrivateMethod(); //this works
   }
}

Отже - якщо абонент - це інший екземпляр мого того ж класу - мій приватний метод фактично доступний ззовні, так би мовити. Це насправді робить не все таким приватним.

У Ruby, з іншого боку, приватний метод насправді мається на увазі бути приватним лише для поточного екземпляра. Ось що забезпечує видалення опції явного приймача.

З іншого боку, я, безумовно, мушу зазначити, що в спільноті Ruby досить поширено взагалі не використовувати ці елементи контролю видимості, враховуючи, що Ruby дає тобі способи їх обійти. На відміну від світу Java, тенденція полягає в тому, щоб зробити все доступним і довіряти іншим розробникам, щоб не накручувати речі.


9
"в спільноті Ruby досить часто взагалі не використовувати ці елементи контролю" - це може бути правдою, але я б сказав, що ми повинні їх використовувати. Як і константи, це не наручники, а спілкування від одного програміста до іншого: "Раджу залишити це в спокої". Ви можете залежати від моїх публічних методів; Я можу змінити свої приватні методи без попередження, оскільки я розглядаю їх деталі реалізації.
Натан Лонг

" У Ruby, з іншого боку, приватний метод насправді мається на увазі бути приватним лише для поточного екземпляра. " Це не правда. Ви все одно можете випадково перезаписати приватні методи з батьківського класу (а деякі класи навіть перераховують це як частину їх API).
Франклін Юй

1
@FranklinYu Це не має жодного стосунку до того, що він написав; конфіденційність у Ruby стосується об'єктів , а не класів , а також методів виклику , а не їх визначення . Приватний метод може бути викликаний лише іншим методом того ж об'єкта; вона не має нічого спільного з тим, що клас цей метод був визначений ст.
philomory

2

Частина причини, по якій доступ до приватних методів можна отримати підкласами в Ruby, полягає в тому, що спадкування Ruby з класами є тонким цукровим покриттям над Module включає - в Ruby, клас, насправді, є своєрідним модулем, який забезпечує успадкування тощо.

http://ruby-doc.org/core-2.0.0/Class.html

Це означає, що в основному підклас "включає" батьківський клас, щоб ефективно функції батьківського класу, включаючи приватні функції , були визначені і в підкласі.

В інших мовах програмування виклик методу передбачає перемикання імені методу до ієрархії батьківського класу та пошуку першого батьківського класу, який відповідає методу. Навпаки, у Ruby, поки ієрархія батьківського класу все ще існує, методи батьківського класу безпосередньо включаються до списку визначених методів підкласу.


2

Порівняння контролів доступу Java до Ruby: Якщо метод оголошений приватним на Java, його можна отримати лише іншими методами у тому ж класі. Якщо метод оголошений захищеним, він може отримати доступ до інших класів, які існують в одному пакеті, а також підкласів класу в іншому пакеті. Коли метод є загальнодоступним, його видно всім. У Java концепція видимості контролю доступу залежить від того, де лежать ці класи в ієрархії спадкування / пакету.

Тоді як у Ruby ієрархія спадкування або пакет / модуль не відповідають. Вся справа в тому, який об’єкт є приймачем методу.

Для приватного методу в Ruby його не можна викликати явним приймачем. Ми можемо (лише) викликати приватний метод із неявним приймачем.

Це також означає, що ми можемо викликати приватний метод з класу, в якому він оголошений, а також у всіх підкласах цього класу.

class Test1
  def main_method
    method_private
  end

  private
  def method_private
    puts "Inside methodPrivate for #{self.class}"
  end
end

class Test2 < Test1
  def main_method
    method_private
  end
end

Test1.new.main_method
Test2.new.main_method

Inside methodPrivate for Test1
Inside methodPrivate for Test2

class Test3 < Test1
  def main_method
    self.method_private #We were trying to call a private method with an explicit receiver and if called in the same class with self would fail.
  end
end

Test1.new.main_method
This will throw NoMethodError

Ніколи не можна викликати приватний метод поза ієрархією класів, де він був визначений.

Захищений метод можна викликати неявним приймачем, як приватний. Крім того, захищений метод також може бути викликаний явним приймачем (лише), якщо приймач є "Я" або "об'єктом одного класу".

 class Test1
  def main_method
    method_protected
  end

  protected
  def method_protected
    puts "InSide method_protected for #{self.class}"
  end
end

class Test2 < Test1
  def main_method
    method_protected # called by implicit receiver
  end
end

class Test3 < Test1
  def main_method
    self.method_protected # called by explicit receiver "an object of the same class"
  end
end


InSide method_protected for Test1
InSide method_protected for Test2
InSide method_protected for Test3


class Test4 < Test1
  def main_method
    Test2.new.method_protected # "Test2.new is the same type of object as self"
  end
end

Test4.new.main_method

class Test5
  def main_method
    Test2.new.method_protected
  end
end

Test5.new.main_method
This would fail as object Test5 is not subclass of Test1
Consider Public methods with maximum visibility

Підсумок

Загальнодоступне: публічні методи мають максимальну видимість

Захищений: захищений метод можна викликати неявним приймачем, як приватний. Крім того, захищений метод також може бути викликаний явним приймачем (лише), якщо приймач є "Я" або "об'єктом одного класу".

Приватний: для приватного методу в Ruby його не можна викликати явним приймачем. Ми можемо (лише) викликати приватний метод із неявним приймачем. Це також означає, що ми можемо викликати приватний метод з класу, в якому він оголошений, а також у всіх підкласах цього класу.


0
First Three types of access specifiers and those define thier scope.
1.Public    ->  Access anywhere out side the class.
2.Private   ->  Can not access outside the class. 
3.Protected ->  This Method not access anywhere this method define 
                scope.

But i have a solution for this problem for all method how to access explain in depth. 

class Test
attr_reader :name
def initialize(name)
  @name = name
end

def add_two(number)
  @number = number 
end

def view_address
  address("Anyaddress")
end

private 
def address(add)
   @add = add
end

protected 
def user_name(name)
  # p 'call method'
  @name = name
end
end

class Result < Test
def new_user
  user_name("test355")
end
end
  1. Список об'єктів
  2. p test = Test.new ("тест")
  3. p test.name
  4. p test.add_two (3)
  5. Елемент списку
  6. p test.view_address
  7. pr = Result.new ("")
  8. p r.new_user

Деякі проблеми редагування коду. Показ другого класу в одному рядку попередньої публікації. Тепер я пояснюю, як отримати доступ до всіх method.First Create Test class object.but приватний метод не може отримати доступ поза класом, а потім отримати доступ до приватного методу. ми створюємо доступ методу view_address через головний об’єкт. а також захищений метод доступу до створення спадщини.
hardik
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.