Mengapa Ruby memiliki metode pribadi dan terlindungi?

141

Sebelum saya membaca artikel ini , saya pikir kontrol akses di Ruby berfungsi seperti ini:

  • public- dapat diakses oleh objek apa pun (misalnya Obj.new.public_method)
  • protected - hanya dapat diakses dari dalam objek itu sendiri, dan juga setiap subclass
  • private - sama seperti dilindungi, tetapi metode ini tidak ada di subkelas

Namun, tampaknya itu protecteddan privatebertindak sama, kecuali untuk kenyataan bahwa Anda tidak dapat memanggil privatemetode dengan penerima eksplisit (yaitu self.protected_methodberfungsi, tetapi self.private_methodtidak).

Apa gunanya ini? Kapan ada skenario ketika Anda tidak ingin metode Anda dipanggil dengan penerima eksplisit?

Kyle Slattery
sumber
3
Jika semua instance Objectdiizinkan untuk memanggil metode pribadi dari setiap instance lainnya Object, akan mungkin untuk mengatakan hal-hal seperti 5.puts("hello world").
sepp2k

Jawaban:

161

protected metode dapat dipanggil dengan instance kelas yang mendefinisikan atau subkelasnya.

privatemetode hanya dapat dipanggil dari dalam objek panggilan. Anda tidak dapat mengakses metode pribadi instance lain secara langsung.

Berikut adalah contoh praktis cepat:

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

some_methodtidak bisa di privatesini. Itu pasti protectedkarena Anda membutuhkannya untuk mendukung penerima eksplisit. Metode pembantu internal Anda biasanya dapat privatekarena mereka tidak perlu dipanggil seperti ini.

Penting untuk dicatat bahwa ini berbeda dari cara kerja Java atau C ++. privatedi Ruby mirip dengan protecteddi Java / C ++ di mana subclass memiliki akses ke metode. Di Ruby, tidak ada cara untuk membatasi akses ke metode dari subkelasnya seperti yang Anda dapat dengan privatedi Java.

Visibilitas di Ruby sebagian besar merupakan "rekomendasi" karena Anda selalu dapat memperoleh akses ke metode menggunakan 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
dbyrne
sumber
9
Ah, oke itu jauh lebih masuk akal. Kesalahpahaman saya berasal dari pemikiran privatevs protectedharus melakukan apakah subclass dapat mewarisi metode, tetapi sebenarnya tentang dari mana metode tersebut dapat dipanggil. Terima kasih!
Kyle Slattery
3
Metode pribadi juga diabaikan secara default oleh RDoc ketika membuat dokumentasi sementara yang terlindungi tidak. Anda selalu dapat menggunakan flag --all untuk memasukkannya.
jasoares
Tetapi jika Anda benar-benar menginginkannya pribadi, tidak bisakah Anda menimpanya send?
Cyoce
78

Perbedaan

  • Siapa pun dapat memanggil metode publik Anda.
  • Anda dapat memanggil metode yang dilindungi, atau anggota kelas Anda yang lain (atau kelas turunan) dapat memanggil metode yang Anda lindungi dari luar. Tidak ada orang lain yang bisa.
  • Hanya Anda yang dapat memanggil metode pribadi Anda, karena mereka hanya dapat dipanggil dengan penerima implisit self. Bahkan Anda tidak dapat menelepon self.some_private_method; Anda harus menelepon private_methoddengan selftersirat.
    • iGEL menunjukkan: "Namun, ada satu pengecualian. Jika Anda memiliki metode pribadi age =, Anda dapat (dan harus) memanggilnya sendiri untuk memisahkannya dari variabel lokal."
    • Sejak Ruby 2.7 , selfpenerima dapat eksplisit, self.some_private_methoddiizinkan. (Penerima eksplisit lainnya masih dianulir, bahkan jika nilai runtime sama dengan self.)

Di Ruby, perbedaan ini hanya saran dari satu programmer ke yang lain. Metode non-publik adalah cara untuk mengatakan "Saya berhak untuk mengubah ini; jangan bergantung padanya." Tetapi Anda masih mendapatkan gunting tajam senddan dapat memanggil metode apa pun yang Anda suka.

Tutorial singkat

# 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

Kemudian Anda dapat menjalankan ruby dwarf.rbdan melakukan ini:

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>
Nathan Long
sumber
8
Penjelasan yang bagus! Namun ada satu pengecualian. Jika Anda memiliki metode pribadi age=, Anda dapat (dan harus) memanggilnya selfuntuk memisahkannya dari variabel lokal.
iGEL
Jika Anda membuat "menyapa" metode yang dilindungi, mengapa Anda tidak bisa melakukan gimli.greet? Karena gimli adalah anggota kelas Dwarf, bukankah seharusnya bisa memanggil metode ini tanpa pelecehan?
JoeyC
@ JoeyC karena ketika Anda melakukannya gimli.greet, gimlibukan penelepon, tetapi penerima. Penelepon adalah "lingkungan eksekusi tingkat atas", yang sebenarnya merupakan instance dari ad-hoc Object. Coba ini:ruby -e 'p self; p self.class'
Kelvin
52

Metode pribadi di Ruby:

Jika suatu metode bersifat pribadi di Ruby, maka itu tidak dapat dipanggil oleh penerima eksplisit (objek). Itu hanya dapat dipanggil secara implisit. Ini dapat disebut secara implisit oleh kelas di mana ia telah dijelaskan dalam serta oleh subkelas dari kelas ini.

Contoh-contoh berikut akan menggambarkannya dengan lebih baik:

1) Kelas Hewan dengan metode private class_name

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

Pada kasus ini:

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

2) Subkelas Hewan bernama Amfibi:

class Amphibian < Animal
  def intro_amphibian
    class_name
  end 
end 

Pada kasus ini:

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

Seperti yang Anda lihat, metode pribadi hanya bisa disebut secara implisit. Mereka tidak dapat dipanggil oleh penerima eksplisit. Untuk alasan yang sama, metode pribadi tidak dapat dipanggil di luar hierarki kelas yang menentukan.

Metode yang Dilindungi di Ruby:

Jika suatu metode dilindungi dalam Ruby, maka ia dapat dipanggil secara implisit oleh kelas yang mendefinisikan dan subkelasnya. Selain itu mereka juga dapat dipanggil oleh penerima eksplisit selama penerima adalah diri atau kelas yang sama dengan diri:

1) Kelas Hewan dengan metode yang dilindungi protect_me

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

Pada kasus ini:

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

2) Kelas mamalia yang diwarisi dari kelas hewan

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

Pada kasus ini

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

3) Kelas amfibi yang diwarisi dari kelas Hewan (sama dengan kelas mamalia)

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

Pada kasus ini

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

4) Kelas yang disebut Tree

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

Pada kasus ini:

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

Pertimbangkan metode pribadi di Jawa. Itu bisa dipanggil dari dalam kelas yang sama, tentu saja, tetapi juga bisa dipanggil dengan instance lain dari kelas yang sama:

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

Jadi - jika penelepon adalah contoh berbeda dari kelas saya yang sama - metode pribadi saya sebenarnya dapat diakses dari "luar", jadi untuk berbicara. Ini sebenarnya membuatnya tampak tidak terlalu pribadi.

Di Ruby, di sisi lain, metode pribadi benar-benar dimaksudkan untuk menjadi pribadi hanya untuk instance saat ini. Inilah yang menghapus opsi dari penerima eksplisit.

Di sisi lain, saya tentu harus menunjukkan bahwa itu cukup umum di komunitas Ruby untuk tidak menggunakan kontrol visibilitas ini sama sekali, mengingat bahwa Ruby memberi Anda cara untuk mengatasinya. Tidak seperti di dunia Java, kecenderungannya adalah membuat segala sesuatu dapat diakses dan memercayai pengembang lain untuk tidak mengacaukan segalanya.

Jacob Mattison
sumber
9
"Cukup umum di komunitas Ruby untuk tidak menggunakan kontrol visibilitas ini sama sekali" - ini mungkin benar, tapi saya katakan kita harus menggunakannya. Seperti konstanta, mereka bukan borgol, tetapi komunikasi dari satu programmer ke yang lain: "Saya menyarankan Anda untuk membiarkan ini sendirian." Anda dapat bergantung pada metode publik saya; Saya dapat mengubah metode pribadi saya tanpa peringatan, karena saya menganggapnya sebagai detail implementasi.
Nathan Long
" Di Ruby, di sisi lain, metode pribadi benar-benar dimaksudkan untuk menjadi pribadi hanya untuk instance saat ini. " Ini tidak benar. Anda masih dapat secara tidak sengaja menimpa metode pribadi dari kelas induk Anda (dan beberapa kelas bahkan mencantumkan ini sebagai bagian dari API mereka).
Franklin Yu
1
@ FranklinYu Itu tidak ada hubungannya dengan apa yang ditulisnya; privasi di Ruby adalah tentang objek , bukan kelas , dan ini tentang memanggil metode, bukan mendefinisikannya . Metode pribadi hanya dapat dipanggil oleh metode lain dari objek yang sama; tidak ada hubungannya dengan kelas apa yang didefinisikan oleh metode ini.
philomory
2

Bagian dari alasan mengapa metode pribadi dapat diakses oleh subclass di Ruby adalah bahwa pewarisan Ruby dengan kelas lebih tipis daripada Modul termasuk - di Ruby, kelas, pada kenyataannya, adalah sejenis modul yang menyediakan warisan, dll.

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

Apa artinya ini adalah bahwa pada dasarnya sebuah subclass "termasuk" kelas induk sehingga secara efektif fungsi-fungsi kelas induk, termasuk fungsi-fungsi pribadi , didefinisikan dalam subkelas juga.

Dalam bahasa pemrograman lain, memanggil metode melibatkan meleburkan nama metode ke hierarki kelas induk dan menemukan kelas induk pertama yang merespons metode tersebut. Sebaliknya, di Ruby, sementara hierarki kelas induk masih ada, metode kelas induk langsung dimasukkan ke dalam daftar metode subkelas yang telah ditentukan.

madumlao
sumber
2

Perbandingan kontrol akses Java terhadap Ruby: Jika metode dinyatakan pribadi di Jawa, itu hanya dapat diakses oleh metode lain dalam kelas yang sama. Jika suatu metode dinyatakan terlindungi, ia dapat diakses oleh kelas-kelas lain yang ada dalam paket yang sama dan juga oleh subkelas kelas dalam paket yang berbeda. Ketika suatu metode bersifat publik, itu dapat dilihat oleh semua orang. Di Jawa, konsep visibilitas kontrol akses tergantung pada di mana kelas-kelas ini terletak pada hierarki warisan / paket.

Sedangkan di Ruby, hierarki warisan atau paket / modul tidak cocok. Ini semua tentang objek mana yang merupakan penerima suatu metode.

Untuk metode pribadi di Ruby, itu tidak pernah bisa dipanggil dengan penerima eksplisit. Kami dapat (hanya) memanggil metode pribadi dengan penerima implisit.

Ini juga berarti kita dapat memanggil metode pribadi dari dalam kelas yang dideklarasikan di dalam serta semua subclass dari kelas ini.

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

Anda tidak pernah dapat memanggil metode pribadi dari luar hirarki kelas di mana ia didefinisikan.

Metode yang dilindungi dapat disebut dengan penerima implisit, seperti halnya pribadi. Selain itu metode yang dilindungi juga dapat dipanggil oleh penerima eksplisit (hanya) jika penerima adalah "diri" atau "objek dari kelas yang sama".

 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

Ringkasan

Publik: Metode publik memiliki visibilitas maksimum

Diproteksi: Metode terproteksi dapat dipanggil dengan penerima implisit, seperti private. Selain itu metode yang dilindungi juga dapat dipanggil oleh penerima eksplisit (hanya) jika penerima adalah "diri" atau "objek dari kelas yang sama".

Pribadi: Untuk metode pribadi di Ruby, itu tidak pernah bisa dipanggil dengan penerima eksplisit. Kami dapat (hanya) memanggil metode pribadi dengan penerima implisit. Ini juga berarti kita dapat memanggil metode pribadi dari dalam kelas yang dideklarasikan di dalam serta semua subclass dari kelas ini.

Neha Chopra
sumber
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. Daftar Objek
  2. p test = Test.new ("test")
  3. p test.name
  4. p test.add_two (3)
  5. Daftar barang
  6. p test.view_address
  7. pr = Result.new ("")
  8. p r.new_user
hardik
sumber
Beberapa masalah dalam mengedit kode. Pertunjukan kelas dua dalam satu baris posting sebelumnya. Sekarang saya jelaskan bagaimana cara mengakses semua method.First Buat kelas Test object.but metode pribadi tidak dapat mengakses kelas luar kemudian mengakses metode pribadi. kami membuat akses metode view_address melalui objek utama. dan juga metode yang dilindungi mengakses pembuatan warisan.
hardik