Ruby neden hem özel hem de korunan yöntemlere sahip?


141

Bu makaleyi okumadan önce , Ruby'deki erişim denetiminin şöyle çalıştığını düşündüm:

  • public- herhangi bir nesne ile erişilebilir (ör. Obj.new.public_method)
  • protected - yalnızca nesnenin kendisinden ve alt sınıflardan erişilebilir
  • private - korunanlarla aynı, ancak yöntem alt sınıflarda mevcut değil

Ancak, anlaşılmaktadır protectedve privateaynı hareket sen diyemezsin gerçeğini hariç privateaçık seçik bir alıcı yöntemleri (yani self.protected_methodeser, ama self.private_methoddeğil).

Bunun amacı ne? Yönteminizin açık bir alıcıyla çağrılmasını istemediğiniz bir senaryo ne zaman?


3
Tüm örneklerinin, Objectdiğer her örneğinin özel yöntemlerini çağırmasına izin verilirse Object, böyle şeyler söylemek mümkün olurdu 5.puts("hello world").
sepp2k

Yanıtlar:


161

protected yöntemler, tanımlayıcı sınıfın veya alt sınıflarının herhangi bir örneği tarafından çağrılabilir.

privateyöntemler yalnızca çağıran nesnenin içinden çağrılabilir. Başka bir örneğin özel yöntemlerine doğrudan erişemezsiniz.

İşte pratik bir örnek:

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

some_methodprivateburada olamaz . protectedAçık alıcıları desteklemek için buna ihtiyacınız olduğu için olmalıdır . Tipik dahili yardımcı yöntemleriniz genellikle privateböyle adlandırılmaları gerekmediği için olabilir.

Bunun Java veya C ++ çalışma biçiminden farklı olduğunu belirtmek önemlidir. privateRuby'de protectedJava / C ++ ile benzerdir , çünkü alt sınıflar yönteme erişebilir. Ruby'de, privateJava'da olduğu gibi bir yönteme alt sınıflarından erişimi kısıtlamanın bir yolu yoktur .

Ruby'de görünürlük büyük ölçüde bir "öneri" dir, çünkü aşağıdakileri kullanarak her zaman bir yönteme erişebilirsiniz 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, bu çok daha mantıklı. Benim yanlış anlama düşünme geldi privatevs protectedbir alt sınıf bir yöntem miras olabilir mi yapmak zorunda, ama bu yöntem çağrılabilir nereye aslında bu. Teşekkürler!
Kyle Slattery

3
Ayrıca korunan yöntemler oluşturulmazken belge oluşturulurken RDoc tarafından özel yöntemler varsayılan olarak yoksayılır. Bunları dahil etmek için her zaman --all bayrağını kullanabilirsiniz.
jasoares

Ama gerçekten özel olmasını istiyorsanız, geçersiz kılamaz sendmısınız?
Cyoce

78

Fark

  • Herkes herkese açık yöntemlerinizi arayabilir.
  • Korumalı yöntemlerinizi arayabilir veya sınıfınızın başka bir üyesi (veya alt sınıf) dışarıdan korumalı yöntemlerinizi çağırabilir. Başka kimse yapamaz.
  • Özel yöntemlerinizi yalnızca siz çağırabilirsiniz, çünkü yalnızca kapalı bir alıcıyla çağrılabilirler self. Hatta arayamam self.some_private_method; zımni private_methodile aramalısınız self.
    • iGEL şu noktalara dikkat çekiyor: Ancak bir istisna var. age = özel yönteminiz varsa, yerel değişkenlerden ayırmak için bunu kendinizle çağırabilirsiniz (ve yapmanız gerekir).
    • Yana Ruby 2.7self alıcısı açık olabilir, self.some_private_methodizin verilir. (Çalışma zamanı değeri aynı olsa bile, diğer açık alıcılara hala izin verilmez self.)

Ruby'de bu ayrımlar sadece bir programcıdan diğerine tavsiye niteliğindedir. Kamuya açık olmayan yöntemler, "Bunu değiştirme hakkını saklıyorum; buna bağlı kalmayın" demenin bir yoludur. Ama hala keskin bir makas alıyorsunuz sendve istediğiniz herhangi bir yöntemi çağırabilirsiniz.

Kısa bir eğitim

# 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

Sonra çalıştırabilir ruby dwarf.rbve bunu yapabilirsiniz:

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
Güzel açıklama! Ancak bir istisna söz konusudur. Özel bir yönteminiz varsa, yerel değişkenlerden ayırmak için bunu age=çağırabilirsiniz (ve çağırmanız gerekir) self.
iGEL

Korumalı bir yöntemi "selamlıyorsanız" neden gimli.greet yapamıyorsunuz? Gimli, Cüce sınıfının bir üyesi olduğundan, bu yöntemi taciz etmeden çağıramaz mı?
JoeyC

@JoeyC bunu yaptığında çünkü gimli.greet, gimliarayan, ancak alıcı değil. Arayan, aslında geçici bir örneği olan "üst düzey yürütme ortamı" dır Object. ruby -e 'p self; p self.class'
Kelvin

52

Ruby'de özel yöntemler:

Ruby'de bir yöntem özelse, açık bir alıcı (nesne) tarafından çağrılamaz. Sadece dolaylı olarak çağrılabilir. İçinde anlatıldığı sınıf ve bu sınıfın alt sınıfları dolaylı olarak çağrılabilir.

Aşağıdaki örnekler bunu daha iyi gösterecektir:

1) Özel yöntem class_name olan bir Animal sınıfı

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

Bu durumda:

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

2) Amfibi adı verilen bir Hayvan alt sınıfı:

class Amphibian < Animal
  def intro_amphibian
    class_name
  end 
end 

Bu durumda:

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

Gördüğünüz gibi, özel yöntemler yalnızca dolaylı olarak çağrılabilir. Açık alıcılar tarafından çağrılamazlar. Aynı nedenle, özel yöntemler tanımlayıcı sınıfın hiyerarşisinin dışında çağrılamaz.

Ruby'de Korumalı Yöntemler:

Bir yöntem Ruby'de korunuyorsa, hem tanımlayıcı sınıf hem de alt sınıfları tarafından dolaylı olarak çağrılabilir. Ayrıca, alıcı kendiliğinden veya kendisiyle aynı sınıfta olduğu sürece açık bir alıcı tarafından da çağrılabilir:

1) Korumalı yöntemi olan bir hayvan sınıfı

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

Bu durumda:

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

2) Hayvan sınıfından miras alınan bir memeli sınıfı

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

Bu durumda

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

3) Hayvan sınıfından miras alınan bir amfibi sınıfı (memeli sınıfı ile aynı)

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

Bu durumda

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

4) Ağaç adı verilen bir sınıf

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

Bu durumda:

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

7

Java'da özel bir yöntem düşünün. Elbette aynı sınıftan çağrılabilir, ancak aynı sınıfın başka bir örneği tarafından da çağrılabilir:

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

Yani - arayan aynı sınıfımın farklı bir örneğiyse - özel yöntemime aslında "dışarıdan" erişilebilir. Bu aslında o kadar da özel görünmüyor.

Öte yandan Ruby'de, özel bir yöntemin gerçekten sadece mevcut örnek için özel olması amaçlanır. Açık bir alıcının seçeneğinin kaldırılması bunu sağlar.

Öte yandan, Ruby toplumunda bu görünürlük kontrollerini hiç kullanmamanın oldukça yaygın olduğunu belirtmeliyim, çünkü Ruby size yine de etraflarında dolaşmanın yollarını veriyor. Java dünyasından farklı olarak, eğilim her şeyi erişilebilir hale getirme ve diğer geliştiricilere işleri mahvetmemeye güvenme eğilimindedir.


9
"Ruby topluluğunda bu görünürlük kontrollerini hiç kullanmamak oldukça yaygın" - bu doğru olabilir, ancak bunları kullanmamız gerektiğini söyleyebilirim. Sabitler gibi, bunlar kelepçe değil, bir programcıdan diğerine bir iletişimdir: "Bunu yalnız bırakmanızı tavsiye ederim." Genel yöntemlerime güvenebilirsiniz; Özel yöntemlerimi uyarı yapmadan değiştirebilirim, çünkü uygulama ayrıntılarını göz önünde bulunduruyorum.
Nathan Long

Öte yandan Ruby'de özel bir yöntemin gerçekten sadece mevcut örnek için özel olması gerekiyor. " Bu doğru değil. Yine de yanlışlıkla üst sınıfınızdan özel yöntemlerin üzerine yazabilirsiniz (ve bazı sınıflar bunu API'larının bir parçası olarak bile listeleyebilir).
Franklin Yu

1
@FranklinYu Bunun yazdıklarına bir etkisi yok; Ruby'de gizlilik sınıflarla değil nesnelerle ilgilidir ve onları tanımlama ile değil çağırma yöntemleriyle ilgilidir . Özel bir yöntem yalnızca aynı nesnenin başka bir yöntemi ile çağrılabilir; yöntemin tanımlandığı sınıfla bir ilgisi yoktur.
philomory

2

Özel yöntemlere Ruby'de alt sınıflar tarafından erişilebilmesinin nedenlerinden biri, sınıflarla Ruby mirasının Modül içerir üzerine ince şeker kaplaması olmasıdır - Ruby'de, aslında bir sınıf, miras sağlayan bir tür modüldür.

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

Bunun anlamı, temel olarak bir alt sınıfın üst sınıfı "içerdiği" ve böylece özel sınıflar da dahil olmak üzere ana sınıfın işlevlerinin alt sınıfta da etkili bir şekilde tanımlanacağı anlamına gelir.

Diğer programlama dillerinde, bir yöntemin çağrılması, yöntemin adının bir üst sınıf hiyerarşisinde köpürmesini ve yönteme yanıt veren ilk üst sınıfı bulmayı içerir. Buna karşılık, Ruby'de, ana sınıf hiyerarşisi hala oradayken, ana sınıfın yöntemleri doğrudan alt sınıfın tanımladığı yöntemler listesine eklenir.


2

Java'nın erişim denetimlerinin Ruby ile karşılaştırılması: Yöntem Java'da özel olarak bildirilirse, yalnızca aynı sınıftaki diğer yöntemlerle erişilebilir. Bir yöntemin korumalı olarak bildirilmesi durumunda, aynı paket içinde var olan diğer sınıfların yanı sıra farklı bir paketteki sınıfın alt sınıfları tarafından erişilebilir. Bir yöntem herkese açık olduğunda herkes tarafından görülebilir. Java'da erişim denetimi görünürlük kavramı, bu sınıfların kalıtım / paket hiyerarşisinde nerede bulunduğuna bağlıdır.

Oysa Ruby'de miras hiyerarşisi veya paket / modül uymuyor. Her şey, hangi nesnenin bir yöntemin alıcısı olduğu ile ilgilidir.

Ruby'de özel bir yöntem için, hiçbir zaman açık bir alıcıyla çağrılamaz. Özel yöntemi örtük bir alıcıyla (yalnızca) çağırabiliriz.

Bu aynı zamanda bildirildiği bir sınıf içinden ve bu sınıfın tüm alt sınıflarından özel bir yöntem çağırabileceğimiz anlamına gelir.

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

Özel yöntemi hiçbir zaman tanımlandığı sınıf hiyerarşisinin dışından çağıramazsınız.

Korumalı yöntem özel gibi örtük bir alıcıyla çağrılabilir. Ayrıca, alıcı "ben" veya "aynı sınıftan bir nesne" ise korunan yöntem (yalnızca) açık bir alıcı tarafından da çağrılabilir.

 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

özet

Genel: Genel yöntemlerin maksimum görünürlüğü vardır

Korumalı: Korumalı yöntem, özel gibi örtük bir alıcıyla çağrılabilir. Ayrıca, alıcı "ben" veya "aynı sınıftan bir nesne" ise korunan yöntem (yalnızca) açık bir alıcı tarafından da çağrılabilir.

Özel: Ruby'deki özel bir yöntem için, hiçbir zaman açık bir alıcıyla çağrılamaz. Özel yöntemi örtük bir alıcıyla (yalnızca) çağırabiliriz. Bu aynı zamanda bildirildiği bir sınıf içinden ve bu sınıfın tüm alt sınıflarından özel bir yöntem çağırabileceğimiz anlamına gelir.


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. Nesne Listesi
  2. p test = Test.new ("test")
  3. p test.name
  4. p testi.ekle_iki (3)
  5. Liste öğesi
  6. p test.view_address
  7. pr = Sonuç.yeni ("")
  8. p r.new_user

Kod düzenleme ile ilgili bazı sorunlar. Önceki yazı tek satırda ikinci sınıf gösterisi. Şimdi tüm yönteme nasıl erişileceğini açıklayacağım. ana nesne üzerinden view_address yöntemine erişim sağlıyoruz. ve ayrıca korunmuş yöntem erişim miras oluşturmak.
hardik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.