Tại sao Ruby có cả phương thức riêng tư và được bảo vệ?


141

Trước khi đọc bài viết này , tôi đã nghĩ rằng kiểm soát truy cập trong Ruby hoạt động như thế này:

  • public- có thể được truy cập bởi bất kỳ đối tượng nào (ví dụ Obj.new.public_method)
  • protected - chỉ có thể được truy cập từ bên trong chính đối tượng, cũng như bất kỳ lớp con nào
  • private - giống như được bảo vệ, nhưng phương thức không tồn tại trong các lớp con

Tuy nhiên, có vẻ như protectedprivatehành động tương tự, ngoại trừ thực tế là bạn không thể gọi privatecác phương thức với một máy thu rõ ràng (nghĩa là self.protected_methodhoạt động, nhưng self.private_methodkhông).

Điểm này là gì? Khi nào có một kịch bản khi bạn không muốn phương thức của mình được gọi với một máy thu rõ ràng?


3
Nếu tất cả các trường hợp Objectđược phép gọi các phương thức riêng tư của mọi trường hợp khác Object, thì có thể nói những điều như thế 5.puts("hello world").
sepp2k

Câu trả lời:


161

protected các phương thức có thể được gọi bởi bất kỳ thể hiện nào của lớp định nghĩa hoặc các lớp con của nó.

privatecác phương thức chỉ có thể được gọi từ bên trong đối tượng gọi. Bạn không thể truy cập trực tiếp các phương thức riêng tư của người khác.

Dưới đây là một ví dụ thực tế nhanh chóng:

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

some_methodkhông thể privateở đây Nó phải là protectedbởi vì bạn cần nó để hỗ trợ người nhận rõ ràng. Các phương thức trợ giúp nội bộ điển hình của bạn thường có thể là privatedo chúng không bao giờ cần phải được gọi như thế này.

Điều quan trọng cần lưu ý là điều này khác với cách Java hoặc C ++ hoạt động. privatetrong Ruby tương tự như protectedtrong Java / C ++ trong đó các lớp con có quyền truy cập vào phương thức. Trong Ruby, không có cách nào để hạn chế quyền truy cập vào một phương thức từ các lớp con của nó như bạn có thể làm với privateJava.

Khả năng hiển thị trong Ruby phần lớn là "khuyến nghị" dù sao vì bạn luôn có thể truy cập vào một phương thức bằng cách sử dụng 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
Ah, ok điều đó có ý nghĩa hơn nhiều. Sự hiểu lầm của tôi xuất phát từ suy nghĩ privateso với protectedviệc phải làm liệu một lớp con có thể kế thừa một phương thức hay không, nhưng thực ra đó là về phương thức có thể được gọi từ đâu. Cảm ơn!
Kyle Slattery

3
Ngoài ra, các phương thức riêng tư được RDoc mặc định bỏ qua khi tạo tài liệu trong khi các phương thức được bảo vệ thì không. Bạn luôn có thể sử dụng cờ --all để bao gồm chúng.
2014

Nhưng nếu bạn thực sự muốn nó riêng tư, bạn có thể ghi đè sendkhông?
Cyoce

78

Sự khác biệt

  • Bất cứ ai cũng có thể gọi phương thức công khai của bạn.
  • Bạn có thể gọi các phương thức được bảo vệ của bạn hoặc một thành viên khác trong lớp của bạn (hoặc một lớp con cháu) có thể gọi các phương thức được bảo vệ của bạn từ bên ngoài. Không ai khác có thể.
  • Chỉ bạn mới có thể gọi các phương thức riêng tư của mình, bởi vì chúng chỉ có thể được gọi với một người nhận ngầm định self. Ngay cả bạn không thể gọi self.some_private_method; bạn phải gọi private_methodvới selfngụ ý.
    • iGEL chỉ ra: "Tuy nhiên, có một ngoại lệ. Nếu bạn có một phương thức private age =, bạn có thể (và phải) tự gọi nó để tách nó khỏi các biến cục bộ."
    • Ruby 2.7 người selfnhận có thể rõ ràng, self.some_private_methodđược cho phép. (Bất kỳ máy thu rõ ràng nào khác vẫn không được phép, ngay cả khi giá trị thời gian chạy giống như self.)

Trong Ruby, những sự phân biệt này chỉ là lời khuyên từ lập trình viên này đến lập trình viên khác. Các phương pháp không công khai là một cách để nói "Tôi bảo lưu quyền thay đổi điều này; đừng phụ thuộc vào nó." Nhưng bạn vẫn có được cái kéo sắc bén sendvà có thể gọi bất kỳ phương pháp nào bạn thích.

Hướng dẫn ngắn gọn

# 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

Sau đó, bạn có thể chạy ruby dwarf.rbvà làm điều này:

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
Giải thích tốt đẹp! Tuy nhiên, có một ngoại lệ. Nếu bạn có một phương thức riêng tư age=, bạn có thể (và phải) gọi nó với selfđể tách nó khỏi các biến cục bộ.
iGEL

Nếu bạn thực hiện "chào" một phương thức được bảo vệ, tại sao bạn không thể thực hiện một gimli.greet? Vì gimli là một thành viên của lớp Người lùn, nên nó không thể gọi phương thức này mà không bị quấy rối?
JoeyC

@JoeyC bởi vì khi bạn làm gimli.greet, gimlikhông phải là người gọi, mà là người nhận. Người gọi là "môi trường thực thi cấp cao nhất", đây thực sự là một trường hợp đặc biệt của Object. Hãy thử điều này:ruby -e 'p self; p self.class'
Kelvin

52

Các phương thức riêng tư trong Ruby:

Nếu một phương thức là riêng tư trong Ruby, thì nó không thể được gọi bởi một người nhận (đối tượng) rõ ràng. Nó chỉ có thể được gọi ngầm. Nó có thể được gọi ngầm bởi lớp mà nó đã được mô tả cũng như bởi các lớp con của lớp này.

Các ví dụ sau sẽ minh họa nó tốt hơn:

1) Một lớp Thú với phương thức riêng class_name

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

Trong trường hợp này:

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

2) Một lớp con của Động vật được gọi là Động vật lưỡng cư:

class Amphibian < Animal
  def intro_amphibian
    class_name
  end 
end 

Trong trường hợp này:

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

Như bạn có thể thấy, các phương thức riêng tư chỉ có thể được gọi một cách ngầm định. Họ không thể được gọi bởi người nhận rõ ràng. Vì lý do tương tự, các phương thức riêng tư không thể được gọi bên ngoài hệ thống phân cấp của lớp định nghĩa.

Các phương pháp được bảo vệ trong Ruby:

Nếu một phương thức được bảo vệ trong Ruby, thì nó có thể được gọi ngầm bởi cả lớp định nghĩa và các lớp con của nó. Ngoài ra, chúng cũng có thể được gọi bởi một người nhận rõ ràng miễn là người nhận tự hoặc cùng lớp với người nhận:

1) Một lớp Động vật với phương thức được bảo vệ bảo vệ

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

Trong trường hợp này:

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

2) Một lớp động vật có vú được thừa hưởng từ lớp động vật

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

Trong trường hợp này

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

3) Một lớp lưỡng cư được thừa hưởng từ lớp Động vật (giống như lớp động vật có vú)

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

Trong trường hợp này

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

4) Một lớp gọi là Cây

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

Trong trường hợp này:

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

7

Hãy xem xét một phương thức riêng tư trong Java. Nó có thể được gọi từ trong cùng một lớp, tất nhiên, nhưng nó cũng có thể được gọi bởi một thể hiện khác của cùng một lớp:

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
   }
}

Vì vậy - nếu người gọi là một thể hiện khác của cùng một lớp - phương thức riêng tư của tôi thực sự có thể truy cập được từ "bên ngoài", có thể nói như vậy. Điều này thực sự làm cho nó dường như không phải tất cả mà riêng tư.

Mặt khác, trong Ruby, một phương thức riêng tư thực sự chỉ có nghĩa là riêng tư đối với trường hợp hiện tại. Đây là những gì loại bỏ tùy chọn của một người nhận rõ ràng cung cấp.

Mặt khác, tôi chắc chắn nên chỉ ra rằng cộng đồng Ruby khá phổ biến khi không sử dụng các điều khiển khả năng hiển thị này, vì Ruby cho bạn cách để đi xung quanh chúng. Không giống như trong thế giới Java, xu hướng là làm cho mọi thứ có thể truy cập được và tin tưởng các nhà phát triển khác không làm hỏng mọi thứ.


9
"Điều khá phổ biến trong cộng đồng Ruby là hoàn toàn không sử dụng các điều khiển hiển thị này" - điều này có thể đúng, nhưng tôi nói chúng ta nên sử dụng chúng. Giống như hằng số, chúng không phải là còng tay, mà là một giao tiếp từ lập trình viên này đến lập trình viên khác: "Tôi khuyên bạn nên để việc này một mình." Bạn có thể phụ thuộc vào phương pháp công khai của tôi; Tôi có thể thay đổi phương thức riêng tư của mình mà không cần cảnh báo, vì tôi xem xét chúng chi tiết triển khai.
Nathan Long

Trong Ruby, mặt khác, một phương pháp tin thực sự được hiểu là tư nhân chỉ đến thể hiện hiện hành. " Đây không phải là sự thật. Bạn vẫn có thể vô tình ghi đè lên các phương thức riêng tư từ lớp cha của bạn (và một số lớp thậm chí liệt kê đây là một phần của API của chúng).
Franklin Yu

1
@FranklinYu Điều đó không ảnh hưởng đến những gì anh ấy đã viết; Quyền riêng tư trong Ruby là về các đối tượng , không phải các lớp và đó là về các phương thức gọi , không xác định chúng. Một phương thức riêng tư chỉ có thể được gọi bằng một phương thức khác của cùng một đối tượng; nó không liên quan gì đến lớp mà phương thức được định nghĩa.
triết học

2

Một phần lý do tại sao các lớp con có thể được truy cập bởi các lớp con trong Ruby là vì sự kế thừa của Ruby với các lớp là lớp phủ mỏng trên Mô-đun bao gồm - trong thực tế, một lớp, là một loại mô-đun cung cấp sự kế thừa, v.v.

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

Điều này có nghĩa là về cơ bản một lớp con "bao gồm" lớp cha mẹ sao cho hiệu quả các hàm của lớp cha, bao gồm cả các hàm riêng , cũng được định nghĩa trong lớp con.

Trong các ngôn ngữ lập trình khác, việc gọi một phương thức bao gồm việc đặt tên phương thức lên một hệ thống phân cấp lớp cha và tìm lớp cha đầu tiên đáp ứng với phương thức đó. Ngược lại, trong Ruby, trong khi hệ thống phân cấp lớp cha vẫn còn đó, các phương thức của lớp cha được đưa trực tiếp vào danh sách các phương thức của lớp con đã được định nghĩa.


2

So sánh các điều khiển truy cập của Java với Ruby: Nếu phương thức được khai báo là riêng tư trong Java, thì chỉ có thể được truy cập bởi các phương thức khác trong cùng một lớp. Nếu một phương thức được khai báo được bảo vệ, nó có thể được truy cập bởi các lớp khác tồn tại trong cùng một gói cũng như bởi các lớp con của lớp trong một gói khác. Khi một phương thức được công khai, nó hiển thị cho mọi người. Trong Java, khái niệm khả năng hiển thị kiểm soát truy cập phụ thuộc vào vị trí của các lớp này trong phân cấp kế thừa / gói.

Trong khi đó, trong Ruby, hệ thống phân cấp thừa kế hoặc gói / mô-đun không phù hợp. Đó là tất cả về đối tượng nào là người nhận phương thức.

Đối với một phương thức riêng tư trong Ruby, nó không bao giờ có thể được gọi với một máy thu rõ ràng. Chúng ta có thể (chỉ) gọi phương thức riêng với một máy thu ngầm.

Điều này cũng có nghĩa là chúng ta có thể gọi một phương thức riêng từ trong một lớp mà nó được khai báo cũng như tất cả các lớp con của lớp này.

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

Bạn không bao giờ có thể gọi phương thức riêng tư từ bên ngoài hệ thống phân cấp lớp nơi nó được định nghĩa.

Phương thức được bảo vệ có thể được gọi với một người nhận ẩn, giống như riêng tư. Ngoài ra, phương thức được bảo vệ cũng có thể được gọi bởi một người nhận rõ ràng (chỉ) nếu người nhận là "bản thân" hoặc "một đối tượng của cùng một lớp".

 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

Tóm lược

Công khai: Phương thức công khai có tầm nhìn tối đa

Được bảo vệ: Phương thức được bảo vệ có thể được gọi với một người nhận ẩn, giống như riêng tư. Ngoài ra, phương thức được bảo vệ cũng có thể được gọi bởi một người nhận rõ ràng (chỉ) nếu người nhận là "bản thân" hoặc "một đối tượng của cùng một lớp".

Riêng tư: Đối với một phương thức riêng tư trong Ruby, nó không bao giờ có thể được gọi bằng một máy thu rõ ràng. Chúng ta có thể (chỉ) gọi phương thức riêng với một máy thu ngầm. Điều này cũng có nghĩa là chúng ta có thể gọi một phương thức riêng từ trong một lớp mà nó được khai báo cũng như tất cả các lớp con của lớp này.


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. Danh sách đối tượng
  2. p test = Test.new ("kiểm tra")
  3. p test.name
  4. p test.add_two (3)
  5. Danh sách mục
  6. p test.view_address
  7. pr = result.new ("")
  8. p r.new_user

Một số vấn đề trong chỉnh sửa mã. Lớp thứ hai hiển thị trong một dòng duy nhất bài trước. Bây giờ tôi giải thích làm thế nào để truy cập tất cả các phương thức. Đầu tiên Tạo đối tượng lớp Test.nhưng phương thức riêng tư không thể truy cập bên ngoài lớp sau đó truy cập phương thức riêng tư. chúng ta tạo truy cập phương thức view_address thông qua đối tượng chính. và cũng bảo vệ phương thức truy cập tạo kế thừa.
hardik
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.