ทำไมทับทิมถึงมีทั้งแบบส่วนตัวและแบบป้องกัน?


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 คล้ายกับprotectedใน Java / C ++ ในคลาสย่อยนั้นมีการเข้าถึงวิธีการ ใน Ruby ไม่มีวิธี จำกัด การเข้าถึงเมธอดจากคลาสย่อยอย่างที่คุณสามารถทำได้privateใน Java

ทัศนวิสัยในทับทิมเป็นส่วนใหญ่ "แนะนำ" อย่างไรก็ตามเนื่องจากคุณสามารถเข้าถึงวิธีใช้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
อ่าโอเคที่เข้าท่ามากกว่านี้ ความเข้าใจผิดของฉันมาจากการคิดprivatevs protectedต้องทำอย่างไรว่าคลาสย่อยสามารถสืบทอดวิธีการได้หรือไม่ ขอบคุณ!
Kyle Slattery

3
นอกจากนี้เมธอดส่วนตัวจะถูกละเว้นโดยค่าเริ่มต้นโดย RDoc เมื่อสร้างเอกสารในขณะที่วิธีการป้องกันไม่ใช่ คุณสามารถใช้ --all ธงเพื่อรวมพวกเขา
jasoares

แต่ถ้าคุณต้องการให้เป็นส่วนตัวจริงๆคุณจะไม่สามารถเอาชนะได้sendหรือไม่?
Cyoce

78

ความแตกต่าง

  • ทุกคนสามารถโทรหาวิธีการสาธารณะของคุณ
  • คุณสามารถเรียกวิธีการป้องกันของคุณหรือสมาชิกคนอื่นของชั้นเรียนของคุณ (หรือชั้นเรียนลูกหลาน) สามารถเรียกวิธีการป้องกันของคุณจากภายนอก ไม่มีใครสามารถทำได้
  • เพียง selfแต่คุณสามารถเรียกวิธีส่วนตัวของคุณเพราะพวกเขาเท่านั้นที่สามารถเรียกว่ามีตัวรับสัญญาณโดยนัยของ แม้คุณไม่สามารถเรียกself.some_private_method; คุณต้องเรียกprivate_methodมีselfนัย
    • iGELชี้ให้เห็น: "มีข้อยกเว้นหนึ่งประการคือถ้าคุณมีเมธอดส่วนตัว age = คุณสามารถ (และต้อง) เรียกมันด้วยตนเองเพื่อแยกมันออกจากตัวแปรท้องถิ่น"
    • ตั้งแต่ทับทิม 2.7selfรับสามารถที่ชัดเจนself.some_private_methodที่ได้รับอนุญาต (ตัวรับสัญญาณที่ชัดเจนอื่น ๆ ยังคงไม่อนุญาตแม้ว่าค่ารันไทม์จะเหมือนกันself)

ใน Ruby ความแตกต่างเหล่านี้เป็นเพียงคำแนะนำจากโปรแกรมเมอร์หนึ่งไปยังอีก วิธีการที่ไม่ใช่แบบสาธารณะเป็นวิธีการพูดว่า "ฉันขอสงวนสิทธิ์ในการเปลี่ยนแปลงสิ่งนี้ไม่ต้องขึ้นอยู่กับมัน" แต่คุณยังคงได้รับกรรไกรคม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 จะไม่สามารถเรียกได้โดยตัวรับที่ชัดเจน (object) สามารถโทรได้โดยปริยาย มันสามารถถูกเรียกโดยปริยายโดยชั้นที่มันได้รับการอธิบายในเช่นเดียวกับโดยคลาสย่อยของชั้นนี้

ตัวอย่างต่อไปนี้จะอธิบายได้ดีกว่า:

1) คลาสของสัตว์ที่มีเมธอด 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

อย่างที่คุณเห็นวิธีการส่วนตัวสามารถเรียกได้โดยปริยายเท่านั้น ไม่สามารถเรียกได้โดยผู้รับที่ชัดเจน ด้วยเหตุผลเดียวกันไม่สามารถเรียกวิธีส่วนตัวนอกลำดับชั้นของคลาสที่กำหนดได้

วิธีการป้องกันในทับทิม:

หากวิธีการได้รับการคุ้มครองในทับทิมก็สามารถเรียกได้ว่าเป็นทั้งชั้นกำหนดและชั้นย่อยของมันโดยปริยาย นอกจากนี้พวกเขายังสามารถถูกเรียกโดยผู้รับที่ชัดเจนตราบใดที่ผู้รับเป็นตัวของตัวเองหรือมีระดับเดียวกันกับของตัวเอง:

1) คลาสสัตว์ที่มีวิธีการป้องกัน protect_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) คลาสสัตว์ครึ่งบกครึ่งน้ำที่สืบทอดจากคลาสสัตว์ (เช่นเดียวกับสัตว์เลี้ยงลูกด้วยนม)

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) คลาสที่ชื่อว่า Tree

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 ที่จะไม่ใช้การควบคุมการมองเห็นเหล่านี้เลย" - นี่อาจเป็นจริง แต่ฉันว่าเราควรใช้มัน เช่นเดียวกับค่าคงที่พวกเขาไม่ใช่กุญแจมือ แต่เป็นการสื่อสารจากโปรแกรมเมอร์หนึ่งไปยังอีกคนหนึ่ง: "ฉันแนะนำให้คุณออกจากเรื่องนี้โดยลำพัง" คุณสามารถพึ่งพาวิธีการสาธารณะของฉัน ฉันอาจเปลี่ยนวิธีส่วนตัวโดยไม่มีการเตือนล่วงหน้าเพราะฉันพิจารณารายละเอียดการใช้งาน
Nathan Long

ในทับทิมในทางกลับกันวิธีการส่วนตัวมีไว้เพื่อเป็นส่วนตัวกับอินสแตนซ์ปัจจุบันเท่านั้น " นี่ไม่เป็นความจริง. คุณยังสามารถเขียนทับวิธีส่วนตัวจากคลาสแม่ของคุณโดยไม่ตั้งใจได้ (และบางคลาสก็ทำรายการนี้เป็นส่วนหนึ่งของ API)
Franklin Yu

1
@ FranklinYu มันไม่มีผลกับสิ่งที่เขาเขียน ความเป็นส่วนตัวในรูบีเป็นเรื่องเกี่ยวกับวัตถุที่ไม่ได้เรียนและมันเป็นเรื่องของการเรียกวิธีการไม่ได้กำหนดให้พวกเขา วิธีการส่วนตัวสามารถเรียกใช้โดยวิธีอื่นของวัตถุเดียวกันเท่านั้น มันไม่มีอะไรเกี่ยวข้องกับคลาสใดที่มีการกำหนดวิธีการไว้
philomory

2

ส่วนหนึ่งของเหตุผลที่ทำไมการเข้าถึงวิธีส่วนตัวสามารถทำได้โดย subclasses ใน Ruby ก็คือการสืบทอด Ruby กับคลาสนั้นบางครั้งจะมี sugarcoating ผ่าน 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 สร้าง Test class object.but วิธีส่วนตัวไม่สามารถเข้าถึงนอกชั้นเรียนแล้วเข้าถึงวิธีส่วนตัว เราสร้างการเข้าถึงวิธีการ view_address ผ่านวัตถุหลัก และยังได้รับการป้องกันวิธีการเข้าถึงการสร้างมรดก
hardik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.