Trong bài viết này, chúng ta sẽ đi sâu vào việc kiểm tra đối tượng. Chúng tôi sẽ giải thích nó là gì, hãy nhìn vào mô-đun Marshall, và sau đó đi qua một ví dụ. Sau đó, chúng tôi sẽ đi sâu hơn một bước và so sánh _dump
và self._load
các phương pháp. Đi thôi!
Object Marshalling là gì?
Khi bạn đang viết mã, bạn có thể muốn lưu một đối tượng và truyền nó sang một chương trình khác hoặc sử dụng lại nó trong lần thực thi chương trình tiếp theo của bạn. Ví dụ, đối tượng sắp xếp được sử dụng trong Sidekiq; khi một công việc Sidekiq được xếp hàng trong ứng dụng Ruby on Rails, thì một chuỗi tuần tự của công việc này - không khác gì một đối tượng - được chèn vào Redis. Sau đó, quy trình Sidekiq có thể giải mã JSON này và tạo lại công việc ban đầu từ JSON.
Trong lập trình máy tính, quá trình tuần tự hóa và giải mã hóa đối tượng này là những gì chúng ta thường gọi là đối tượng sắp xếp . Bây giờ, hãy xem xét những gì Ruby cung cấp ban đầu để xử lý Object Marshalling.
Mô-đun Thống chế
Vì Ruby là một ngôn ngữ lập trình hướng đối tượng hoàn toàn, nó cung cấp một cách để tuần tự hóa và lưu trữ các đối tượng bằng cách sử dụng Marshall
mô-đun trong thư viện tiêu chuẩn của nó. Nó cho phép bạn tuần tự hóa một đối tượng thành một luồng byte có thể được lưu trữ và giải mã hóa trong một quy trình Ruby khác.
Vì vậy, hãy tuần tự hóa một chuỗi và xem xét kỹ hơn đối tượng được tuần tự hóa.
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
Sau đó, chúng tôi gọi Marshal.dump
phương thức mô-đun để tuần tự hóa chuỗi của chúng tôi. Chúng tôi lưu trữ giá trị trả về — chứa chuỗi được tuần tự hóa của chúng tôi — trong serialized_string
Biến đổi. Chuỗi này có thể được lưu trữ trong một tệp và tệp có thể được sử dụng lại để tạo lại đối tượng ban đầu trong một quy trình khác. Sau đó, chúng tôi gọi Marshal.load
phương thức để tạo lại đối tượng ban đầu từ luồng byte.
Chúng ta có thể thấy rằng chuỗi mới được hoàn nguyên này có một object_id
khác hơn hello_world
chuỗi, có nghĩa là một đối tượng khác, nhưng chứa cùng một dữ liệu.
Tuyệt đấy! Nhưng thế nào là Marshal
mô-đun có thể tái tạo lại chuỗi không? Và, điều gì sẽ xảy ra nếu tôi muốn có quyền kiểm soát các thuộc tính để tuần tự hóa và giải mã hóa?
Một ví dụ cụ thể về Object Marshalling
Để trả lời những câu hỏi này, hãy triển khai chiến lược sắp xếp trên một cấu trúc tùy chỉnh có tên là User
.
User = Struct.new(:fullname, :age, :roles)
user = User.new('Mehdi Farsi', 42, [:admin, :operator])
Người dùng User
struct xác định 3 thuộc tính:fullname
, age
và roles
. Đối với ví dụ này, chúng tôi có một quy tắc kinh doanh trong đó chúng tôi chỉ tuần tự hóa khi nó phù hợp với các tiêu chí sau:
-
fullname
chứa ít hơn 64 ký tự -
roles
mảng không chứa:admin
vai trò
Để làm như vậy, chúng tôi có thể xác định một User#marshal_dump
phương pháp để thực hiện chiến lược tuần tự hóa tùy chỉnh của chúng tôi. Phương thức này sẽ được gọi khi chúng ta gọi Marshal.dump
phương thức với một phiên bản của User
struct dưới dạng tham số. Hãy xác định phương pháp này:
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/"
Trong ví dụ trên, chúng ta có thể thấy rằng User#marshal_dump
của chúng tôi phương thức được gọi khi chúng ta gọi Marshal.dump (người dùng). user_dump
biến chứa chuỗi là chuỗi tuần tự hóa của User
của chúng tôi ví dụ.
Bây giờ chúng ta đã có bãi chứa của mình, hãy giải mã hóa nó để khôi phục lại người dùng của chúng ta. Để làm như vậy, chúng tôi xác định một User#marshal_load
phương pháp chịu trách nhiệm triển khai chiến lược deserialization của User
đổ.
Vì vậy, hãy xác định phương pháp này.
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=[]>
Trong ví dụ trên, chúng ta có thể thấy rằng phương pháp User#marshal_load method
của chúng tôi được gọi khi chúng ta gọi Marshal.load(user_dump)
. original_user
biến chứa một cấu trúc là sự tái tạo của phiên bản người dùng của chúng tôi.
Lưu ý rằng original_user.roles
không tương tự với user.roles
mảng kể từ trong quá trình tuần tự hóa, user.roles
bao gồm :admin
vai diễn. Vì vậy, user.roles
chưa được tuần tự hóa thành user_dump
biến.
Phương thức _dump và self._load
Khi Marshal.dump
và Marshal.load
được gọi, các phương thức này gọi marshal_dump
và marshal_load
các phương thức trên đối tượng được truyền dưới dạng tham số của các phương thức này.
Nhưng, điều gì sẽ xảy ra nếu tôi nói với bạn rằng Marshal.dump
và Marshal.load
các phương thức cố gắng gọi hai phương thức khác có tên là _dump
và self._load
trên đối tượng được truyền dưới dạng tham số?
Phương pháp _dump
Sự khác biệt giữa marshal_dump
và _dump
phương pháp là:
- bạn cần xử lý chiến lược tuần tự hóa ở cấp thấp hơn khi sử dụng
_dump
- bạn cần trả về một chuỗi đại diện cho dữ liệu để tuần tự hóa -
marshal_dump
phương thức được ưu tiên hơn_dump
nếu cả hai đều được xác định
Hãy xem ví dụ sau:
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"
Trong User#_dump
, chúng tôi phải khởi tạo và trả về đối tượng tuần tự hóa - chuỗi đại diện cho quá trình tuần tự hóa của bạn.
Trong ví dụ sau, chúng tôi xác định User#marshal_dump
và User#_dump
và trả về một chuỗi để xem phương thức nào được gọi là
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"
Chúng tôi có thể thấy rằng chỉ có User#marshal_dump
được gọi ngay cả khi cả hai đều được xác định.
Phương thức self._load
Bây giờ, hãy xem marshal_load
và _load
phương pháp.
Sự khác biệt giữa marshal_load
và _load
phương pháp là:
- Bạn cần xử lý chiến lược deserialization ở cấp độ thấp hơn khi sử dụng
_load
- Bạn chịu trách nhiệm khởi tạo đối tượng gốc. -
marshal_load
phương thức lấy một đối tượng deserialized làm đối số khi_self.load
phương thức lấy chuỗi được tuần tự hóa làm đối số. -
marshal_load
phương thức là một phương thức phiên bản khiself._load
là một phương thức lớp.
Hãy xem ví dụ sau:
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=[]>
Trong User._load
phương pháp:
- chúng tôi giải mã chuỗi được trả về bởi
User#_dump
phương pháp - chúng tôi khởi tạo một
User
mới bằng cách chuyển thông tin được giải thích trên không
Chúng tôi có thể thấy rằng chúng tôi chịu trách nhiệm phân bổ và khởi tạo đối tượng được sử dụng để tạo lại người dùng ban đầu của chúng tôi.
Vì vậy, Marshal.load
được kết hợp với marshal_load
đảm nhận việc khởi tạo đối tượng ban đầu được hoàn nguyên. Sau đó, nó gọi marshal_load
phương thức với đối tượng được tuần tự hóa được truyền dưới dạng đối số trên đối tượng mới được khởi tạo.
Ngược lại, một cuộc gọi tới Marshal.load
được kết hợp với _load
cho phép self._load
phương pháp lớp do:
- giải mã dữ liệu được trả về bởi
_dump
phương pháp - khởi tạo đối tượng ban đầu đã hoàn nguyên
Kết luận
Tùy thuộc vào nhu cầu của mình, bạn có thể quyết định triển khai chiến lược tuần tự hóa / giải mã hóa cao hơn hoặc thấp hơn. Để làm như vậy, bạn có thể sử dụng mô-đun Marshal kết hợp với các phương pháp móc câu Marshal thích hợp.
Voilà!