ในบทความนี้ เราจะมาเจาะลึกเรื่องการจัดการวัตถุ เราจะอธิบายว่ามันคืออะไร ดูโมดูล Marshall แล้วดูตัวอย่าง จากนั้นเราจะเจาะลึกลงไปอีกขั้นและเปรียบเทียบ _dump
และ self._load
วิธีการ ไปกันเถอะ!
Object Marshalling คืออะไร
เมื่อคุณกำลังเขียนโค้ด คุณอาจต้องการบันทึกอ็อบเจ็กต์และส่งไปยังโปรแกรมอื่นหรือนำมาใช้ใหม่ในการรันโปรแกรมครั้งต่อไปของคุณ การจัดวางวัตถุใช้ใน Sidekiq ตัวอย่างเช่น เมื่องาน Sidekiq ถูกจัดคิวในแอปพลิเคชัน Ruby on Rails การทำให้เป็นอนุกรมของงานนี้ — ซึ่งไม่มีอะไรมากไปกว่าอ็อบเจกต์ — จะถูกแทรกใน Redis กระบวนการ Sidekiq จะสามารถดีซีเรียลไลซ์ JSON นี้และสร้างงานเดิมจาก JSON ขึ้นใหม่ได้
ในการเขียนโปรแกรมคอมพิวเตอร์ กระบวนการทำให้เป็นอนุกรมและดีซีเรียลไลซ์เซชันของอ็อบเจ็กต์คือสิ่งที่เราเรียกกันทั่วไปว่า object marshalling . ตอนนี้ มาดูกันว่า Ruby มีไว้เพื่อจัดการกับ Object Marshalling อย่างไร
โมดูลจอมพล
เนื่องจาก Ruby เป็นภาษาการเขียนโปรแกรมเชิงวัตถุอย่างสมบูรณ์ จึงมีวิธีการทำให้เป็นอนุกรมและจัดเก็บวัตถุโดยใช้ Marshall
โมดูลในไลบรารีมาตรฐาน ช่วยให้คุณสามารถทำให้เป็นอนุกรมของวัตถุไปยังสตรีมไบต์ที่สามารถจัดเก็บและยกเลิกการทำให้เป็นอนุกรมในกระบวนการ Ruby อื่นได้
ดังนั้น เรามาทำให้เป็นอนุกรมของสตริงและดูวัตถุที่จัดลำดับให้ละเอียดยิ่งขึ้น
hello_world = 'hello world!'
serialized_string = Marshal.dump(hello_world) # => "\x04\bI\"\x11hello world!\x06:\x06ET"
serialized_string.class # => String
deserialized_hello_world = Marshal.load(serialized_string) # => "hello world!"
hello_world.object_id # => 70204420126020
deserialized_hello_world.object_id # => 70204419825700
จากนั้นเราเรียก Marshal.dump
วิธีการโมดูลเพื่อทำให้เป็นอนุกรมของสตริงของเรา เราเก็บค่าส่งคืนซึ่งมีสตริงที่ทำให้เป็นอนุกรมของเราใน serialized_string
ตัวแปร. สตริงนี้สามารถเก็บไว้ในไฟล์และสามารถใช้ไฟล์ซ้ำเพื่อสร้างออบเจ็กต์เดิมในกระบวนการอื่นได้ จากนั้นเราเรียก Marshal.load
วิธีการสร้างอ็อบเจ็กต์ดั้งเดิมจากไบต์สตรีม
เราจะเห็นว่าสตริงที่สร้างใหม่นี้มี object_id
. ที่แตกต่างกัน กว่า hello_world
string ซึ่งหมายความว่าเป็นวัตถุอื่น แต่มีข้อมูลเดียวกัน
สวยเย็น! แต่อย่างไร Marshal
โมดูลสามารถสร้างสตริงใหม่ได้หรือไม่? แล้วถ้าฉันต้องการควบคุมแอตทริบิวต์ที่จะจัดลำดับและดีซีเรียลไลซ์ให้มีลักษณะเป็นอย่างไร
ตัวอย่างที่เป็นรูปธรรมของการมาร์แชลวัตถุ
ในการตอบคำถามเหล่านี้ เรามาปรับใช้กลยุทธ์การจัดวางในโครงสร้างแบบกำหนดเองที่ชื่อว่า User
.
User = Struct.new(:fullname, :age, :roles)
user = User.new('Mehdi Farsi', 42, [:admin, :operator])
User
struct กำหนดแอตทริบิวต์ 3 อย่าง:fullname
, age
และ roles
. สำหรับตัวอย่างนี้ เรามีกฎทางธุรกิจที่เราจัดลำดับเมื่อตรงกับเกณฑ์ต่อไปนี้เท่านั้น:
- The
fullname
มีอักขระน้อยกว่า 64 ตัว - บทบาท
roles
อาร์เรย์ไม่มี:admin
บทบาท
ในการทำเช่นนั้น เราสามารถกำหนด User#marshal_dump
เพื่อใช้กลยุทธ์การจัดลำดับแบบกำหนดเองของเรา เมธอดนี้จะถูกเรียกเมื่อเราเรียกใช้ Marshal.dump
เมธอดที่มีอินสแตนซ์ User
จัดโครงสร้างเป็นพารามิเตอร์ มานิยามวิธีนี้กัน:
User = Struct.new(:age, :fullname, :roles) do
def marshal_dump
{}.tap do |result|
result[:age] = age
result[:fullname] = fullname if fullname.size <= 64
result[:roles] = roles unless roles.include? :admin
end
end
end
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
user_dump = Marshal.dump(user) # 'in User#marshal_dump'
user_dump # => "\x04\bU:\tUser{\a:\bageI\"\x10Mehdi Farsi\x06:\x06ET:\rfullnamei/"
ในตัวอย่างข้างต้น เราจะเห็นว่า User#marshal_dump
. ของเรา เมธอดจะถูกเรียกเมื่อเราเรียกใช้ Marshal.dump(ผู้ใช้) user_dump
ตัวแปรมีสตริงที่เป็นอนุกรมของ User
. ของเรา ตัวอย่าง
ตอนนี้เรามีการถ่ายโอนข้อมูลแล้ว ให้ทำการดีซีเรียลไลซ์ข้อมูลเพื่อสร้างผู้ใช้ของเราใหม่ ในการทำเช่นนั้น เรากำหนด User#marshal_load
เมธอดซึ่งมีหน้าที่ในการปรับใช้กลยุทธ์ดีซีเรียลไลเซชันของ User
ทิ้ง
มานิยามวิธีนี้กัน
User = Struct.new(:age, :fullname, :roles) do
def marshal_dump
{}.tap do |result|
result[:age] = age
result[:fullname] = fullname if fullname.size <= 64
result[:roles] = roles unless roles.include? :admin
end
end
def marshal_load(serialized_user)
self.age = serialized_user[:age]
self.fullname = serialized_user[:fullname]
self.roles = serialized_user[:roles] || []
end
end
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
user_dump = Marshal.dump(user) # 'in User#marshal_dump'
user_dump # => "\x04\bU:\tUser{\a:\bagei/:\rfullnameI\"\x10Mehdi Farsi\x06:\x06ET"
original_user = Marshal.load(user_dump) # 'in User#marshal_load'
original_user # => #<struct User age=42, fullname="Mehdi Farsi", roles=[]>
ในตัวอย่างข้างต้น เราจะเห็นว่า User#marshal_load method
. ของเรา ถูกเรียกเมื่อเราเรียกใช้ Marshal.load(user_dump)
. original_user
ตัวแปรมีโครงสร้างที่สร้างอินสแตนซ์ผู้ใช้ของเราใหม่
โปรดทราบว่า original_user.roles
ไม่เหมือนกับ user.roles
อาร์เรย์ตั้งแต่ในระหว่างการทำให้เป็นอันดับ user.roles
รวม :admin
บทบาท. ดังนั้น user.roles
ไม่ถูกทำให้เป็นอนุกรมใน user_dump
ตัวแปร
วิธีการ _dump และ self._load
เมื่อ Marshal.dump
และ Marshal.load
ถูกเรียกใช้ เมธอดเหล่านี้เรียก marshal_dump
และ marshal_load
เมธอดบนอ็อบเจ็กต์ที่ส่งผ่านเป็นพารามิเตอร์ของเมธอดเหล่านี้
แต่ถ้าฉันบอกคุณว่า Marshal.dump
และ Marshal.load
เมธอดพยายามเรียกเมธอดอีกสองวิธีชื่อ _dump
และ self._load
บนวัตถุที่ส่งผ่านเป็นพารามิเตอร์?
วิธีการ _dump
ความแตกต่างระหว่าง marshal_dump
และ _dump
วิธีการคือ:
- คุณต้องจัดการกลยุทธ์การทำให้เป็นอันดับที่ต่ำกว่าเมื่อใช้
_dump
method — คุณต้องส่งคืนสตริงที่แสดงถึงข้อมูลเพื่อทำให้เป็นอนุกรม - the
marshal_dump
เมธอดมีความสำคัญเหนือ_dump
ถ้าทั้งสองถูกกำหนดไว้
มาดูตัวอย่างต่อไปนี้กัน:
User = Struct.new(:age, :fullname, :roles) do
def _dump level
[age, fullname].join(':')
end
end
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
Marshal.dump(user) # => "\x04\bIu:\tUser\x1342:Mehdi Farsi\x06:\x06EF"
ใน User#_dump
เมธอด เราต้องสร้างอินสแตนซ์และส่งคืนอ็อบเจ็กต์การทำให้เป็นอนุกรม — สตริงที่แสดงถึงการทำให้เป็นอนุกรมของคุณ
ในตัวอย่างต่อไปนี้ เรากำหนด User#marshal_dump
และ User#_dump
เมธอดและส่งคืนสตริงเพื่อดูว่าเมธอดใดเรียกว่า
User = Struct.new(:age, :fullname, :roles) do
def marshal_dump
'in User#marshal_dump'
end
def _dump level
'in User#_dump'
end
end
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
user_dump = Marshal.dump(user) # "in User#marshal_dump"
เราจะเห็นว่าเฉพาะ User#marshal_dump
ถูกเรียกแม้ว่าจะมีการกำหนดทั้งสองไว้ก็ตาม
วิธีการโหลดตัวเอง
ทีนี้มาดูที่ marshal_load
และ _load
วิธีการ
ความแตกต่างระหว่าง marshal_load
และ _load
วิธีการคือ:
- คุณต้องจัดการกลยุทธ์ดีซีเรียลไลเซชันในระดับที่ต่ำกว่าเมื่อใช้
_load
method — คุณมีหน้าที่สร้างอินสแตนซ์ของวัตถุดั้งเดิม - The
marshal_load
เมธอดรับวัตถุดีซีเรียลไลซ์เป็นอาร์กิวเมนต์เมื่อ_self.load
เมธอดรับสตริงที่ต่อเนื่องกันเป็นอาร์กิวเมนต์ - The
marshal_load
เมธอดเป็นเมธอดอินสแตนซ์เมื่อself._load
เป็นวิธีการเรียน
ลองมาดูตัวอย่างต่อไปนี้:
User = Struct.new(:age, :fullname, :roles) do
def _dump level
[age, fullname].join(':')
end
def self._load serialized_user
user_info = serialized_user.split(':')
new(*user_info, Array.new)
end
end
user = User.new(42, 'Mehdi Farsi', [:admin, :operator])
user_dump = Marshal.dump(user)
user_dump # => "\x04\bIu:\tUser\x1342:Mehdi Farsi\x06:\x06EF"
original_user = Marshal.load(user_dump)
original_user # => #<struct User age="Mehdi Farsi", fullname=42, roles=[]>
ใน User._load
วิธีการ:
- เราทำการดีซีเรียลไลซ์สตริงที่ส่งคืนโดย
User#_dump
วิธีการ - เราสร้าง
User
ใหม่ โดยส่งข้อมูลดีซีเรียลไลซ์
เราเห็นได้ว่าเรามีหน้าที่ในการจัดสรรและสร้างอินสแตนซ์ของอ็อบเจ็กต์ที่ใช้สร้างผู้ใช้เดิมของเราใหม่
ดังนั้น Marshal.load
ควบคู่ไปกับ marshal_load
ดูแลการสร้างอินสแตนซ์ของวัตถุดั้งเดิมที่สร้างใหม่ จากนั้นจะเรียก marshal_load
เมธอดที่มีวัตถุต่อเนื่องเป็นอาร์กิวเมนต์บนวัตถุที่สร้างอินสแตนซ์ใหม่
ตรงกันข้าม เรียก Marshal.load
ควบคู่ไปกับ _load
ให้ self._load
วิธีการเรียนอยู่ในความดูแลของ:
- ดีซีเรียลไลซ์ข้อมูลที่ส่งคืนโดย
_dump
วิธีการ - สร้างตัวอย่างวัตถุดั้งเดิมที่สร้างขึ้นใหม่
บทสรุป
ขึ้นอยู่กับความต้องการของคุณ คุณสามารถตัดสินใจใช้กลยุทธ์การทำให้เป็นอนุกรม/ดีซีเรียลไลซ์เซชั่นที่สูงขึ้นหรือต่ำลงได้ ในการดำเนินการดังกล่าว คุณสามารถใช้โมดูล Marshal ร่วมกับวิธี Marshal hook ที่เหมาะสมได้
ว้าว!