นี่คือเรื่องราวทั้งหมดโดยอธิบายถึงแนวคิดการเขียนโปรแกรมเมตาที่จำเป็นเพื่อทำความเข้าใจว่าเหตุใดการรวมโมดูลจึงทำงานได้เหมือนใน Ruby
จะเกิดอะไรขึ้นเมื่อรวมโมดูล
การรวมโมดูลลงในคลาสจะเป็นการเพิ่มโมดูลให้กับบรรพบุรุษของคลาส คุณสามารถดูบรรพบุรุษของคลาสหรือโมดูลใดก็ได้โดยเรียกancestors
ใช้เมธอด:
module M
def foo; "foo"; end
end
class C
include M
def bar; "bar"; end
end
C.ancestors
เมื่อคุณเรียกใช้เมธอดบนอินสแตนซ์C
Ruby จะดูทุกรายการของรายการบรรพบุรุษนี้เพื่อค้นหาวิธีการอินสแตนซ์ที่มีชื่อที่ระบุ เนื่องจากเรารวมM
เข้าC
, M
อยู่ในขณะนี้เป็นบรรพบุรุษของC
ดังนั้นเมื่อเราเรียกว่าfoo
อินสแตนซ์ของC
ทับทิมจะพบวิธีการที่ในM
:
C.new.foo
โปรดทราบว่าการรวมไม่ได้คัดลอกอินสแตนซ์หรือเมธอดคลาสใด ๆ ไปยังคลาสแต่เพียงเพิ่ม "โน้ต" ให้กับคลาสซึ่งควรมองหาวิธีการอินสแตนซ์ในโมดูลที่รวมไว้ด้วย
แล้วเมธอด "คลาส" ในโมดูลของเราล่ะ?
เนื่องจากการรวมจะเปลี่ยนแปลงวิธีการส่งอินสแตนซ์เท่านั้นการรวมโมดูลลงในคลาสทำให้เมธอดอินสแตนซ์พร้อมใช้งานในคลาสนั้นเท่านั้น วิธีการ "คลาส" และการประกาศอื่น ๆ ในโมดูลจะไม่ถูกคัดลอกไปยังคลาสโดยอัตโนมัติ:
module M
def instance_method
"foo"
end
def self.class_method
"bar"
end
end
class C
include M
end
M.class_method
C.new.instance_method
C.class_method
Ruby ใช้วิธีการเรียนอย่างไร
ในทับทิมเรียนและโมดูลเป็นวัตถุธรรมดา - พวกเขาเป็นกรณีของการเรียนและClass
Module
ซึ่งหมายความว่าคุณสามารถสร้างคลาสใหม่แบบไดนามิกกำหนดให้กับตัวแปร ฯลฯ :
klass = Class.new do
def foo
"foo"
end
end
klass.new.foo
นอกจากนี้ใน Ruby คุณมีความเป็นไปได้ในการกำหนดวิธีการแบบซิงเกิลตันบนวัตถุ วิธีการเหล่านี้ถูกเพิ่มเป็นวิธีการอินสแตนซ์ใหม่ในคลาส singletonพิเศษที่ซ่อนอยู่ของอ็อบเจ็กต์:
obj = Object.new
def obj.foo
"foo"
end
obj.singleton_class.instance_methods(false)
แต่คลาสและโมดูลก็ไม่ใช่แค่วัตถุธรรมดาเช่นกัน? ในความเป็นจริงพวกเขา! นั่นหมายความว่าพวกเขาสามารถมีวิธีการแบบซิงเกิลตันได้เช่นกัน? ใช่! และนี่คือวิธีการกำเนิดคลาส:
class Abc
end
def Abc.foo
"foo"
end
Abc.singleton_class.instance_methods(false)
หรือวิธีทั่วไปในการกำหนดเมธอดคลาสคือการใช้self
ภายในบล็อกนิยามคลาสซึ่งอ้างถึงคลาสอ็อบเจ็กต์ที่กำลังสร้าง:
class Abc
def self.foo
"foo"
end
end
Abc.singleton_class.instance_methods(false)
ฉันจะรวมเมธอดคลาสไว้ในโมดูลได้อย่างไร
ในขณะที่เราเพิ่งสร้างเมธอดคลาสเป็นเพียงวิธีการอินสแตนซ์บนคลาสซิงเกิลตันของคลาสอ็อบเจ็กต์ หมายความว่าเราสามารถรวมโมดูลลงในคลาสซิงเกิลตันเพื่อเพิ่มเมธอดคลาสได้หรือไม่? ใช่!
module M
def new_instance_method; "hi"; end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
self.singleton_class.include M::ClassMethods
end
HostKlass.new_class_method
นี้self.singleton_class.include M::ClassMethods
สายไม่ได้ดูดีมากดังนั้นทับทิมเพิ่มObject#extend
ซึ่งไม่เหมือนกัน - คือรวมถึงโมดูลเข้าไปในชั้นเดี่ยวของวัตถุนี้:
class HostKlass
include M
extend M::ClassMethods
end
HostKlass.singleton_class.included_modules
การย้ายการextend
โทรไปยังโมดูล
ตัวอย่างก่อนหน้านี้ไม่ใช่โค้ดที่มีโครงสร้างดีด้วยเหตุผลสองประการ:
- ตอนนี้เราต้องเรียกทั้งสอง
include
และextend
ในHostClass
คำจำกัดความเพื่อให้โมดูลของเรารวมอย่างถูกต้อง สิ่งนี้จะยุ่งยากมากหากคุณต้องรวมโมดูลที่คล้ายกันจำนวนมาก
HostClass
การอ้างอิงโดยตรงM::ClassMethods
ซึ่งเป็นรายละเอียดการใช้งานของโมดูลM
ที่HostClass
ไม่จำเป็นต้องรู้หรือสนใจ
แล้วสิ่งนี้: เมื่อเราเรียกinclude
บรรทัดแรกเราจะแจ้งให้โมดูลทราบว่ามีการรวมและยังให้คลาสอ็อบเจ็กต์ของเราด้วยเพื่อให้สามารถเรียกextend
ตัวเองได้ วิธีนี้เป็นหน้าที่ของโมดูลที่จะต้องเพิ่มเมธอดคลาสหากต้องการ
นี่คือสิ่งที่เป็นself.included
วิธีพิเศษสำหรับ Ruby เรียกเมธอดนี้โดยอัตโนมัติเมื่อใดก็ตามที่โมดูลรวมอยู่ในคลาสอื่น (หรือโมดูล) และส่งผ่านอ็อบเจ็กต์คลาสโฮสต์เป็นอาร์กิวเมนต์แรก:
module M
def new_instance_method; "hi"; end
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def new_class_method; "hello"; end
end
end
class HostKlass
include M
def self.existing_class_method; "cool"; end
end
HostKlass.singleton_class.included_modules
self.included
ของหลักสูตรการเพิ่มวิธีการเรียนไม่ได้เป็นเพียงสิ่งเดียวที่เราสามารถทำได้ใน เรามีคลาสออบเจ็กต์ดังนั้นเราจึงสามารถเรียกใช้เมธอด (คลาส) อื่น ๆ ได้:
def self.included(base)
base.existing_class_method
end