Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ตามวิกิพีเดีย การเขียนโปรแกรมเชิงวัตถุ (OOP) เป็นกระบวนทัศน์การเขียนโปรแกรมตามแนวคิดของ "วัตถุ" ซึ่งสามารถมีข้อมูลและรหัส:ข้อมูลในรูปแบบของฟิลด์ (มักเรียกว่าแอตทริบิวต์หรือคุณสมบัติ) และรหัสในรูปแบบ ของขั้นตอนต่างๆ (มักเรียกว่า method)

Ruby เป็นภาษาเชิงวัตถุล้วนๆ ซึ่งหมายความว่าในภาษา Ruby ทุกอย่างเป็นวัตถุ อ็อบเจ็กต์เหล่านี้ ไม่ว่าจะเป็นสตริง ตัวเลข คลาส โมดูล ฯลฯ ทำงานในระบบที่เรียกว่า The Object Model .

Ruby เสนอวิธีการที่เรียกว่า object_id ซึ่งสามารถใช้ได้กับวัตถุทั้งหมด ตัวระบุนี้ส่งคืนจำนวนเต็มและจะไม่เหมือนกันสำหรับสองอ็อบเจ็กต์ มาทำให้มือของเราสกปรกกันเถอะ คุณสามารถทำได้โดยพิมพ์ irb ในเทอร์มินัลของคุณ

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่เห็นด้านบน สตริง จำนวนเต็ม อาร์เรย์ คลาส และแม้แต่เมธอดล้วนเป็นอ็อบเจ็กต์ เนื่องจากมี ID อ็อบเจ็กต์

ในบทความนี้ เราจะกล่าวถึงแนวคิดต่อไปนี้โดยละเอียด:

  • คลาสและอินสแตนซ์
  • มรดก
  • วิธีการสาธารณะ ส่วนตัว และได้รับการป้องกัน
  • ส่วนผสม
  • โมดูล
  • ลำดับชั้นวัตถุ

คลาสและอินสแตนซ์

ใน Ruby คลาสคือตำแหน่งที่กำหนดคุณลักษณะและทัศนคติ (การกระทำ) ของวัตถุ หากวัตถุเป็นทรงกลม (แอตทริบิวต์) และควรจะสามารถพูดได้ (การกระทำ) เราสามารถบอกได้จากชั้นเรียนว่าเป็นของสิ่งนั้น เพราะแอตทริบิวต์และการกระทำเหล่านี้ถูกกำหนดให้เป็นสิ่งที่เรียกว่า วิธีการ ในคลาสนั้น อ็อบเจ็กต์ที่เป็นของคลาสเรียกว่า อินสแตนซ์ ของคลาสนั้นและสร้างขึ้น (ทันที) โดยใช้ .new . มาเริ่มกันด้วยการสร้างคลาสที่ชื่อว่า Human . ฉันคิดว่าเราทุกคนเป็นมนุษย์ ดังนั้นเรื่องนี้น่าจะสนุก

class Human
  def initialize(name)
    @name = name
  end
end

เริ่มต้น วิธีการระบุข้อกำหนดสำหรับอินสแตนซ์ใหม่ของคลาสที่จะสร้าง ในกรณีข้างต้น เราจะเห็นได้ว่าการสร้างมนุษย์ใหม่จำเป็นต้องมีชื่อ ดังนั้น สามารถสร้างตัวอย่างใหม่ของมนุษย์ได้โดยใช้คำสั่ง Human.new(name) ที่ชื่ออะไรก็ได้ที่คุณเลือก ในกรณีของเรา ให้ใช้ 'Henry' เพื่อทดสอบสิ่งนี้ในสภาพแวดล้อม irb ของเรา สิ่งที่เราต้องทำคือโหลดไฟล์โดยใช้คำสั่ง load './path_to_filename' และใช้คำสั่งซ้ำเพื่อดำเนินการไฟล์ใหม่ทุกครั้งที่มีการเปลี่ยนแปลง ในกรณีของฉัน มันคือ load './Human.rb' เนื่องจาก irb เปิดอยู่ในโฟลเดอร์ที่มีไฟล์ดังกล่าว ในการรันโค้ดโดยไม่ใช้ irb เราต้องเพิ่ม puts คำสั่งก่อนทุกคำสั่งเพื่อให้เห็นผล

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

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

@name ตัวแปรถูกเรียกเป็นตัวแปรอินสแตนซ์เนื่องจาก @ สัญลักษณ์ที่ขึ้นต้นด้วย ซึ่งหมายความว่าตัวแปรนี้สามารถอ้างอิงได้โดยวิธีอื่นใดในคลาส ตราบใดที่อินสแตนซ์ของคลาสที่เป็นปัญหายังคงมีอยู่ ที่นี่ เราได้สร้างและตั้งค่าให้เท่ากับชื่อที่ใช้เริ่มต้นวัตถุ

มาดำเนินการกำหนดลักษณะและการกระทำของวัตถุใด ๆ ที่เป็นของคลาสนี้ เนื่องจากวัตถุที่สร้างขึ้นเรียกว่าอินสแตนซ์ของคลาส เมธอดที่กำหนดพฤติกรรมจึงเรียกว่า เมธอดอินสแตนซ์ . มนุษย์มีชื่อและส่วนต่างๆ ของร่างกายและสามารถดำเนินการบางอย่างได้ ดังนั้นมากำหนดกัน

def name
  @name
end

def no_of_legs
  2
end

def no_of_hands
  2
end

def speak
  'blablabla'
end

เราได้เพิ่มวิธีการที่เรียกชื่อของมนุษย์ที่สร้างขึ้น กำหนดจำนวนขาและมือที่มนุษย์มี และให้มนุษย์สามารถพูดได้ เราสามารถเรียกเมธอดเหล่านี้บนอินสแตนซ์ของคลาสได้โดยใช้ instance.method_name ดังที่แสดงด้านล่าง

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

จะเกิดอะไรขึ้นถ้าเราเปลี่ยนใจและตัดสินใจว่าเราต้องการเปลี่ยนชื่อของเฮนรี่ในชั้นเรียนของเรา Ruby มีวิธีการในตัวที่เราสามารถทำได้ แต่ก็สามารถทำได้ด้วยตนเองเช่นกัน ด้วยตนเอง เราสามารถเปลี่ยนแปลง name method ของเราจากการเป็นเพียง getter method ที่ได้รับชื่อเป็น setter method ที่ตั้งค่าเป็นค่าด้วยถ้ามีให้

def name=(new_name)
  @name = new_name
end

การใช้ attr_accessor ในตัวของ Ruby วิธีเราสามารถละทิ้งวิธีการชื่อของเราและแทนที่ด้วยบรรทัด attr_accessor :name :

class Human
  attr_accessor :name

  def initialize(name)
    @name = name
  end
# rest of the code
end

ไม่ว่าจะเลือกวิธีการใด สุดท้ายนี้คือสิ่งที่หาได้

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

วิธีการทั้งหมดที่สร้างขึ้นจนถึงขณะนี้เรียกว่า instance methods เพราะสามารถเรียกได้ในทุกอินสแตนซ์ของคลาส แต่ไม่ใช่ตัวคลาสเอง นอกจากนี้ยังมีสิ่งที่เรียกว่า class method ซึ่งสามารถเรียกใช้ในคลาสได้เอง ไม่ใช่ในอินสแตนซ์ ตั้งชื่อเมธอดของคลาสโดยนำหน้าชื่อเมธอดด้วย self. . นี่คือตัวอย่าง:

# within the Human class
def self.introduction
  "I am a human, not an alien!"
end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่เราเห็นข้างต้นว่า class method ใช้ได้เฉพาะกับ class เองเท่านั้น ไม่สามารถใช้กับ instance ได้ นอกจากนี้ เช่นเดียวกับตัวแปรอินสแตนซ์ เรามีตัวแปรคลาส ซึ่งนำหน้าด้วย @ สองตัว สัญลักษณ์ ตัวอย่างแสดงอยู่ด้านล่าง

class Human
  attr_accessor :name
  @@no_cars_bought = 0 #class variable

  def initialize(name)
    @name = name
  end

  def self.no_cars_bought
    @@no_cars_bought
  end

  def buy_car
    @@no_of_cars_bought += 1
    "#{@name} just purchased a car"
  end
end

ในตัวอย่างนี้ เราได้เพิ่ม buy_car วิธีการเพื่อให้มนุษย์ทุกคนสามารถซื้อรถได้ นอกจากนี้เรายังได้สร้างตัวแปรคลาสที่เรียกว่า @@no_of_cars_bought ที่จะเพิ่มขึ้น 1 ทุกครั้งที่มีการซื้อรถ สุดท้ายนี้ เราได้สร้างวิธีการเรียนที่เรียกว่า no_cars_bought ที่ดึงจำนวนรถยนต์ที่ซื้อ มาดูกันว่ามันทำงานอย่างไร:

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

วิธีการทั้งหมดที่กำหนดไว้จนถึงขณะนี้เรียกว่า สาธารณะ เมธอดเพราะสามารถเข้าถึงได้จากภายนอกชั้นเรียน นอกจากนี้เรายังสามารถกำหนดวิธีการที่เรียกว่า ส่วนตัว เมธอดซึ่งสามารถเข้าถึงได้ภายในคลาสเท่านั้น ลองมาดูตัวอย่างกัน

# at the bottom of the Human class
  def say_account_number
    "My account number is #{account_number}"
  end

  private

  def account_number
    "1234567890"
  end

ได้ผลลัพธ์ดังนี้:

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

เมื่อเราเรียก henry.account_number เราได้รับ "NoMethodError" เพราะ account_number เป็นเมธอดส่วนตัวที่สามารถเข้าถึงได้จากภายในคลาสเท่านั้น เมื่อเข้าใช้งานผ่าน say_account_number อย่างที่เราทำ ไม่มีข้อผิดพลาด เนื่องจากวิธีนี้มีอยู่ในคลาสเดียวกับวิธีส่วนตัว สิ่งสำคัญคือต้องสังเกตว่าทุกอินสแตนซ์เมธอดหลัง private คำหลักกลายเป็นวิธีการส่วนตัว ดังนั้นควรกำหนดเมธอดส่วนตัวที่ด้านล่างของคลาสหลังจากเมธอดสาธารณะทั้งหมด

คุณเคยได้ยินเกี่ยวกับ protected วิธีการ? ช่ายยย! สิ่งเหล่านี้ก็มีเช่นกัน แต่เราจะพูดถึงพวกเขาหลังจากที่เราเข้าใจแนวคิดของ มรดก .

มรดก

เมื่อเรารู้เกี่ยวกับคลาสและอินสแตนซ์แล้ว เรามาพูดถึงเรื่อง inheritance . กัน . เพื่อให้เข้าใจแนวคิดเรื่องการสืบทอดอย่างถูกต้อง เรามาสร้างคลาสใหม่ชื่อ Mammal เนื่องจากมนุษย์เป็นสัตว์เลี้ยงลูกด้วยนม ฉันจำวลีหนึ่งในชั้นเรียนวิทยาศาสตร์ได้:"มนุษย์ทุกคนเป็นสัตว์เลี้ยงลูกด้วยนม แต่ไม่ใช่สัตว์เลี้ยงลูกด้วยนมทั้งหมดที่เป็นมนุษย์" ฉันยังจำได้ด้วยว่าลักษณะบางอย่างของสัตว์เลี้ยงลูกด้วยนมนั้นรวมถึงการมีขนหรือขนและสมองที่ซับซ้อน มาใส่ข้อมูลนี้ใน Mammal ชั้นเรียน

class Mammal
  def has_hair?
    "Most certainly, Yes"
  end

  def has_complex_brain?
    "Well, as a mammal, what do you expect?"
  end
end

คุณจำวลีชั้นเรียนวิทยาศาสตร์ของฉันได้ไหม ถ้าเป็นเช่นนั้น เป็นสิ่งสำคัญที่เราจะต้องสร้างความสัมพันธ์ที่ยืนยันคำสั่งนี้ ดังนั้นสิ่งที่เราจะทำ? เราอนุญาตให้คลาสมนุษย์สืบทอดคุณสมบัติของ Mammal คลาสโดยเพิ่ม < Mammal เป็นคำจำกัดความของคลาส

class Human < Mammal
  # all other code
end

คลาสที่สืบทอดคุณสมบัติของอีกคลาสหนึ่งเรียกว่า subclass และคลาสที่สืบทอดมานั้นเรียกว่า superclass . ในกรณีของเรา Human เป็นคลาสย่อยและ Mammal คือซุปเปอร์คลาส สิ่งสำคัญที่ควรทราบ ณ จุดนี้ หากคุณกำลังกำหนดคลาสทั้งหมดในไฟล์เดียวเหมือนที่เราทำอยู่ในขณะนี้ Mammal คำจำกัดความของคลาสควรมาก่อน Human ในไฟล์ของคุณ เนื่องจากเราไม่ต้องการอ้างถึงตัวแปรก่อนที่จะกำหนด มาดูกันว่าตอนนี้มนุษย์มีฟีเจอร์อะไรเพิ่มเติมบ้าง

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น ขณะนี้มนุษย์สามารถเข้าถึงคุณลักษณะทั้งหมดที่กำหนดไว้ใน Mammal ระดับ. หากเราลบมรดก (เช่น เราลบ < Mammal จากโค้ดบรรทัดนั้น) และรันคำสั่ง henry.class.superclass เราได้รับ "วัตถุ" เป็นคำตอบของเรา สิ่งนี้บอกเราว่าทุกคลาสเมื่อไม่ได้สืบทอดโดยตรงจากคลาสอื่นมีซูเปอร์คลาสเป็น "Object" ซึ่งช่วยเสริมความจริงที่ว่าใน Ruby แม้แต่คลาสก็เป็นวัตถุ

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ตอนนี้เรารู้แล้วว่า superclasses คืออะไร คราวนี้ก็เป็นเวลาที่เหมาะสมที่จะพูดถึงคีย์เวิร์ด super . Ruby จัดเตรียมคีย์เวิร์ดนี้เพื่อเปิดใช้งานการใช้ซ้ำและแก้ไขเมธอดที่มีอยู่แล้วในซูเปอร์คลาส ใน Mammal superclass จำได้ว่าเรามี method ที่เรียกว่า has_hair?; จะเป็นอย่างไรถ้าเราต้องการเพิ่มข้อมูลเพิ่มเติมเฉพาะสำหรับมนุษย์ทุกครั้งที่มีการเรียกใช้เมธอดนั้น นี่คือที่มาของการใช้ super keyword ใน Human . ของเรา class เรากำหนด method ที่มีชื่อเดียวกันว่า has_hair? .

def has_hair?
  super + ", but humans can be bald at times"
end

เมื่อเรียก super keyword แล้ว Ruby จะค้นหาชื่อเมธอดนั้นใน superclass และส่งคืนผลลัพธ์ ในวิธีการข้างต้น เราได้เพิ่มข้อมูลเพิ่มเติมให้กับผลลัพธ์ที่สร้างโดย has_hair? ของ superclass วิธีการ

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

Ruby รองรับการสืบทอดคลาสเดียวเท่านั้น ซึ่งหมายความว่าคุณสามารถสืบทอดคุณสมบัติของคลาสจากคลาสเดียวเท่านั้น ฉันแน่ใจว่าคุณกำลังสงสัยว่าจะเกิดอะไรขึ้นถ้าคุณต้องการเพิ่มคุณสมบัติหลายรายการที่ไม่เฉพาะเจาะจงสำหรับชั้นเรียนเฉพาะในชั้นเรียนของคุณ Ruby ยังจัดเตรียมสิ่งนี้ในรูปแบบของ mixins แต่ก่อนจะพูดถึงเรื่องนี้ เรามาพูดถึง วิธีป้องกัน . กันก่อน .

วิธีป้องกัน

เมธอดที่ได้รับการป้องกันทำงานเหมือนกับเมธอดส่วนตัวที่เรียกใช้งานได้ภายในคลาสและคลาสย่อย มาสร้างวิธีการป้องกันภายใน Mammal ชั้นเรียน

  #at the bottom of the Mammal class
  def body_status
    body
  end

  protected
  def body
    "This body is protected"
  end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น เราไม่สามารถเข้าถึง body วิธีเพราะมีการป้องกัน อย่างไรก็ตาม เราสามารถเข้าถึงได้จาก body_status method ซึ่งเป็นอีกวิธีหนึ่งใน Mammal ระดับ. เรายังสามารถเข้าถึงเมธอดที่ได้รับการป้องกันจากคลาสย่อยของคลาสที่กำหนดไว้ มาลองทำกันใน Human ชั้นเรียน

# within the Human class
def check_body_status
  "Just a human checking that " + body.downcase
end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น ไม่ว่าจะกำหนดวิธีการของร่างกายภายใน Human คลาสและได้รับการป้องกัน Human class สามารถเข้าถึงได้เพราะเป็น subclass ของ Mammal ระดับ. การเข้าถึงคลาสย่อยนี้สามารถทำได้ด้วยเมธอดส่วนตัว อย่างไรก็ตาม สิ่งนี้นำไปสู่คำถามว่าวิธีการเหล่านี้มีความแตกต่างกันอย่างไร

วิธีการป้องกันสามารถเรียกได้โดยใช้ตัวรับที่ชัดเจน เช่น receiver.protected_method ตราบใดที่ผู้รับที่เป็นปัญหาคือคำหลัก self หรืออยู่ในคลาสเดียวกับ self . อย่างไรก็ตาม วิธีส่วนตัวสามารถเรียกได้โดยใช้ตัวรับที่ชัดเจนก็ต่อเมื่อผู้รับที่ชัดเจนคือ self มาแก้ไขโค้ดของเราโดยแทนที่ body.downcase ด้วย self.body.downcase ใน check_body_status เมธอดและเปลี่ยนการเรียกเมธอดส่วนตัวของเราให้รวม self .

def check_body_status
  "Just a human checking that " + self.body.downcase
  # body method is protected
end

def say_account_number
  "My account number is #{self.account_number}"
  # account_number method is private
end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ป.ล. ก่อน Ruby 2.7 เมื่อเรียกใช้เมธอดส่วนตัวเพื่อรับข้อมูลบางอย่างโดยใช้ self เป็นไปไม่ได้

มาแทนที่คำว่า self . กัน ด้วยวัตถุในคลาสเดียวกับ self . ในกรณีของเรา self คือ Henry ที่กำลังเรียกใช้เมธอด และ Henry เป็นตัวอย่างของ Human คลาสที่สืบทอดมาจาก Mammal ชั้นเรียน

def check_body_status
  non_self = Human.new('NotSelf') #same class as self
  puts "Just #{non_self.name} checking that " + non_self.body.downcase
  # Mammal.new is also in the same class as self
  "Just a human checking that " + Mammal.new.body.downcase
end

def say_account_number
  non_self = Human.new('NotSelf')
  "My account number is #{non_self.account_number}"
end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น สามารถแทนที่ self . ได้ ในเมธอดที่ได้รับการป้องกันด้วยอ็อบเจ็กต์ของคลาสเดียวกับ self แต่วิธีนี้เป็นไปไม่ได้ด้วยวิธีการส่วนตัว

ส่วนผสม

มิกซ์อินคือชุดของโค้ดที่กำหนดไว้ใน โมดูล ซึ่งเมื่อรวมอยู่ในหรือขยายไปยังชั้นเรียนใด ๆ แล้ว ให้ความสามารถเพิ่มเติมแก่ชั้นเรียนนั้น สามารถเพิ่มโมดูลหลายโมดูลในคลาสได้ เนื่องจากไม่มีข้อจำกัดในเรื่องนี้ ซึ่งแตกต่างจากที่ได้รับจากการสืบทอดคลาส จากข้อมูลนี้ เรามาสร้างโมดูลที่ชื่อว่า Movement เพื่อเพิ่มความสามารถในการเคลื่อนไหวให้กับคลาสมนุษย์

module Movement
  def hop
    "I can hop"
  end

  def swim
    "Ermmmm, I most likely can if I choose"
  end
end

ขั้นตอนต่อไปคือการรวมโมดูลนี้ไว้ใน Human . ของเรา ระดับ. ทำได้โดยการเพิ่มวลี include <module_name> ถึงชั้นเรียนที่เป็นปัญหา ในกรณีของเรา สิ่งนี้จะนำมาซึ่งการเพิ่ม include Movement แก่ชนชั้นมนุษย์ดังแสดงข้างล่างนี้

class Human < Mammal
  include Movement
  # rest of the code
end

มาทดสอบโค้ดนี้กัน:

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น เมธอดที่ผสมลงในคลาสผ่านโมดูลจะใช้ได้เฉพาะกับอินสแตนซ์ของคลาสเท่านั้น ไม่ใช่ตัวคลาสเอง จะเป็นอย่างไรถ้าเราต้องการทำให้เมธอดในโมดูลพร้อมใช้งานในคลาสและไม่ใช่อินสแตนซ์ ในการทำเช่นนี้ เราจะแทนที่คำว่า "รวม" ด้วย "ขยาย" เช่นเดียวกับใน extend Movement .

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

.extend สามารถใช้เทอมกับอินสแตนซ์ของคลาสได้ แต่ในลักษณะที่แตกต่างกันมาก มาสร้างโมดูลอื่นที่เรียกว่า Parent .

module Parent
  def has_kids?
    "most definitely"
  end
end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงด้านบน โดยใช้ .extend(Parent) ในตัวแปร dad ทำให้ has_kids? วิธีการที่มีอยู่ สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อเราไม่ต้องการให้เมธอดของโมดูลผสมกันในทุกอินสแตนซ์ของคลาส ดังนั้น extend สามารถใช้ได้เฉพาะกรณีที่เราสนใจเท่านั้น

โมดูล

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

module Male
  AGE = "Above 0"

  class Adult
    def initialize
      puts "I am an adult male"
    end
  end

  def self.hungry
    puts "There's no such thing as male food, I just eat."
  end

  module Grown
    def self.age
      puts "18 and above"
    end
  end
end

คุณอาจสังเกตเห็นว่าวิธีการที่กำหนดและเรียกใช้ภายในโมดูลด้านบนนั้นนำหน้าด้วย self . นี่เป็นเพราะว่าโมดูลไม่สามารถสร้างอินสแตนซ์ได้ และเมธอดใดๆ ที่กำหนดไว้ภายในโมดูลนั้น โดยไม่มี self คำนำหน้าจะใช้ได้เป็นมิกซ์อินเท่านั้น ดังที่เราเห็นก่อนหน้านี้เมื่อเราพูดถึง mixins . ในการเรียกใช้เมธอดในโมดูลนั้น เราต้องระบุสิ่งนี้ในชื่อเมธอดโดยใช้ self.method_name . จำได้ว่าเรายังใช้ self คีย์เวิร์ดในคลาสเมธอด หลักการเดียวกันนี้ใช้ที่นี่

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น ในการเข้าถึงคลาส โมดูล หรือค่าคงที่ที่กำหนดไว้ภายในโมดูล เราใช้ module_name::target_name . เพื่อให้เข้าใจแนวคิดเนมสเปซอย่างถูกต้อง เราจะสร้างโมดูลอื่นที่มีคลาสชื่อ Adult แล้วมาดูกันว่าทั้งสองคลาสมีความแตกต่างกันอย่างไร

module Female
  class Adult
    def initialize
      puts "I am an adult female"
    end
  end
    def self.hungry
      puts "Maybe there's such a thing as female food, I'm not sure. I just need to eat"
    end
end

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น เรามีสองชั้นเรียนที่มีชื่อ "ผู้ใหญ่" โดยการรวมไว้ในโมดูลของตนเอง เราสามารถใช้ชื่อเหล่านี้ได้โดยไม่สับสนเกี่ยวกับคลาสสำหรับผู้ใหญ่ที่ถูกเรียกในแต่ละครั้ง นอกจากนี้ โค้ดของเราอ่านง่ายและมีระเบียบมากขึ้น และเราใช้การแยกหลักการออกแบบข้อกังวล ขณะที่เราแยกบล็อคโค้ดต่างๆ ออกเป็นหมวดหมู่ต่างๆ ตามฟังก์ชัน

ลำดับชั้นของออบเจ็กต์

การเข้าใจคลาส อินสแตนซ์ โมดูล และแนวคิดเรื่องการสืบทอดนั้นน่าทึ่งมาก แต่ความรู้ในด้านนี้ยังไม่สมบูรณ์หากไม่เข้าใจลำดับชั้นของออบเจ็กต์ใน Ruby หมายถึงลำดับการค้นหาเมธอดใน Ruby มีวิธีการบางอย่างที่ช่วยให้เราเข้าใจลำดับชั้นนี้ หนึ่งในนั้นคือ ancestors กระบวนการ. เมธอดนี้ไม่สามารถใช้ได้กับอินสแตนซ์ของคลาส แต่สามารถเรียกบนคลาสเองและบรรพบุรุษได้ ตัวอย่างแสดงอยู่ด้านล่าง

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

จากผลของ Human.ancestors เราเห็นว่าเมธอดนี้ส่งคืนคลาสที่เป็นปัญหา รวมโมดูลโดยตรง คลาสพาเรนต์ และคลาสของพาเรนต์รวมโมดูลโดยตรง วนซ้ำนี้ต่อไปจนกว่าจะถึง Basic Object ซึ่งเป็นรากของวัตถุทั้งหมด

อีกวิธีหนึ่งที่ใช้ได้เพื่อรับข้อมูลเพิ่มเติมเกี่ยวกับคลาสคือเมธอด included_modules .

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น คลาสมนุษย์มีสองโมดูลที่รวมอยู่ในนั้น:โมดูลที่เรารวมโดยตรงโดยใช้ include Movement และสิ่งที่รวมอยู่ในคลาสอ็อบเจ็กต์ ซึ่งหมายความว่าแต่ละคลาสจะสืบทอดคุณสมบัติของคลาสจากคลาสบรรพบุรุษ และแต่ละโมดูลที่รวมอยู่ในนั้นจะถูกรวมเข้าด้วย จากข้อมูลนี้ เราจะทำแบบฝึกหัดง่ายๆ เพื่อยืนยันเส้นทางการค้นหาเมธอดใน Ruby และคลาสใดที่มีลำดับความสำคัญ เหนือผู้อื่นในเส้นทางนี้ เราจะกำหนดเมธอดที่มีชื่อเดียวกันแต่มีสตริงเอาต์พุตต่างกันและวางไว้ใน Human คลาส Movement โมดูล และ Mammal ชั้นเรียน

# in the mammal class
def find_path
  "Found me in the Mammal class path"
end

# in the Movement module
def find_path
  "Found me in the Movement module path"
end
# in the Human class
def find_path
  "Found me in the Human class path"
end

มาทำแบบฝึกหัดนี้กัน

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น ลำดับการวางบรรพบุรุษเมื่อเราเรียก .ancestors บนคลาสคือพาธที่ตามมาเมื่อมองหาเมธอดที่เรียกใช้บนอินสแตนซ์ของคลาสนั้น สำหรับ Human ตัวอย่างคลาสที่เราสร้างขึ้น Ruby ค้นหาเมธอดในคลาสก่อน หากไม่พบ จะไปยังโมดูลที่รวมโดยตรง หากไม่พบ มันจะไปยังซูเปอร์คลาสและวงจรจะดำเนินต่อไปจนกว่าจะถึง BasicObject . หากไม่พบวิธีการที่นั่น แสดงว่า "NoMethodError" ถูกส่งกลับ โดยใช้ .ancestors เมธอด เราสามารถระบุเส้นทางการค้นหาสำหรับออบเจ็กต์และตำแหน่งของเมธอดที่เล็ดลอดออกมาจากจุดใดก็ได้

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

ทำความเข้าใจกับ Ruby Object Model ในเชิงลึก

ดังที่แสดงไว้ข้างต้น ตัวแปร Henry มีวิธีการมากมาย อย่างไรก็ตาม เมื่อเราลบออกจากสิ่งที่มีอยู่ใน Object คลาส เราพบว่าเราเหลือเฉพาะที่เรากำหนดไว้โดยเฉพาะในไฟล์ของเรา และวิธีการอื่น ๆ ทุกวิธีได้รับการสืบทอด สิ่งนี้จะเหมือนกันสำหรับทุกอ็อบเจ็กต์ คลาสของมัน และบรรพบุรุษของมัน ทำให้มือของคุณสกปรกได้โดยลองใช้ชุดค่าผสมต่างๆ ที่เกี่ยวข้องกับ Human.methods , Mammal.methods , Module.methods และคลาสหรือโมดูลอื่นๆ ที่กำหนดไว้ก่อนหน้านี้ คุณจะพบว่าสิ่งนี้ช่วยให้คุณเข้าใจโมเดลวัตถุ Ruby มากขึ้น