Giap Hiep

I'm Giap Hiep

I'm a web developer, a gymer. I enjoy share something i know that help people's work!
Giap Hiep

Deep copy trong ruby

Việc copy giá trị trong Ruby thường cần thiết. Điều này có vẻ đơn giản nếu object được copy đơn giản. Nhưng nếu bạn phải copy một object với cấu trúc gồm nhiều mảng hoặc hash, bạn sẽ gặp một số vấn đề.

Objects and References

Để hiểu chuyện gì đang xảy ra, hãy xem vài ví dụ đơn giản. Đầu tiên, phép gán sử dụng trong Ruby.

a = 1
b = a
a += 1
puts b

Ở đây phép gán đang lấy giá trị của a và gán giá trị đó cho b sử dụng phép gán. Bất kỳ thay đổi nào đến a sẽ không phản liên quan đến b. Nhưng hãy đến ví dụ phức tạp hơn chút nữa?

a = [1, 2]
b = a
a << 3
puts b.inspect

Trước khi chạy các câu lệnh trên, thử đoán output là gì và tại sao. Không giống như ví dụ trước, khi thay a thay đổi thì b cũng thay đổi theo, nhưng lí do là gì? Bởi vì mảng trong Ruby không thuộc loại POD. Phép gán không copy giá trị mà chỉ đơn giản là copy tham chiếu đến mảng ban đầu mà b trỏ tới trên vùng nhớ. Lúc này ab đang trỏ đến cùng một đối tượng mảng, bất kỳ thay đổi nào lên mỗi biến thì biến còn lại sẽ thấy được trên biến kia.

Bây giờ, bạn đã biết tại sao copy các đối tượng non-trivial với những tham chiếu đến đối tượng khác có thể khó khăn. Nếu đơn giản copy một object nghĩa là bạn đang copy một tham chiếu từ object đó đến object sâu hơn, vì vậy bạn copy đó gọi là "shllow copy"

dup và clone

Ruby cung cấp 2 method phục vụ cho việc copy. Object#dup sẽ tạo ra "shallow copy" của object. Để làm được, method dup sẽ gọi method initialize__copy của class đó. Ở một vài class giống như Array, nó sẽ khởi tạo một array mới với cùng số phần tử như mảng gốc. Tuy nhiên nó không phải là deep copy.
Xem ví dụ sau:

a = [1, 2]
b = a.dup
a << 3
puts b.inspect
a = [[1, 2]]
b = a.dup
a[0] << 3
puts b.inspect

Cái gì bên trong đang xảy ra? Method Array#initialize_copy sẽ tạo một bản sao của một mảng, nhưng bản copy đó là shallow copy. Nếu bạn có bất kỳ loại non-POD trong mảng của bạn, sử dụng dup chỉ deep copy một phần. Nó chỉ deep copy mảng đầu tiên (mảng cha), bất kỳ các mảng sâu hơn(bên trong), hash hoặc các object khác sẽ chỉ là shallow copy.

Có một phương pháp khác đang nói là clone. Method clone thực hiện điều tương tự như method dup với khác biệt quan trọng: dự kiến các đối tượng sẽ ghi đè method này bằng một method khác có thể thực hiện deep copy.

Trong thực tế điểu này có nghĩa là gì? Mỗi class bạn có thể định nghĩa một method clone, điều đó sẽ tạo ra deep copy. Có nghĩa bạn phải viết method clone cho mỗi class bạn thực hiện.