Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Hiểu Phương Thức Freeze Ruby: Giải Thích Tính Thay Đổi Đối Tượng

Đối tượng có tính thay đổi (mutable) nghĩa là gì?

Đừng để từ ngữ sang trọng làm rối bạn, "tính thay đổi" (mutability) chỉ có nghĩa trạng thái nội bộ đối tượng có thể thay đổi. Đây là mặc định của tất cả đối tượng, trừ những đối tượng đã đóng băng (frozen), hoặc thuộc danh sách đối tượng đặc biệt.

Nói cách khác, không phải tất cả đối tượng Ruby đều thay đổi được!

Ví dụ:

Không có lý do gì để số, symbol, hoặc ngay cả true/false (cũng là đối tượng) thay đổi.

Số 1 luôn là 1.

Nhưng đối tượng khác, đặc biệt dùng lưu dữ liệu, như Array hay Hash, nên có khả năng thay đổi vì lý do hiệu năng.

Lựa chọn thay thế?

Bạn có thể tạo bản sao mới của đối tượng với thay đổi và trả về đối tượng mới, để nguyên đối tượng gốc.

Nếu mảng bất biến (immutable) và bạn muốn thay đổi một phần tử, phải sao chép tất cả dữ liệu, kể cả phần tử không thay đổi.

Hãy tưởng tượng phải sao chép mảng một triệu (hoặc hơn) phần tử mỗi lần thay đổi, dù nhỏ! Không hiệu quả…

Anyway.

Hãy nhìn sâu hơn cách tính thay đổi hoạt động trong Ruby.

Tính Thay Đổi & Biến Là Con Trỏ

Có loại lỗi lập trình do kết hợp hai điều:

  • Đối tượng thay đổi được.
  • Biến không chứa dữ liệu trực tiếp, mà tham chiếu đến nơi lưu dữ liệu.

Cách lỗi thể hiện khi bạn thử 'alias' biến.

Ví dụ:

name = "Peter"
other_name = name
puts other_name # "Peter"

Trong ví dụ, cả name và other_name chứa tham chiếu đến cùng đối tượng chuỗi. Bạn dùng cái nào cũng được để hiển thị hoặc sửa đổi nội dung chuỗi.

Hiểu Phương Thức Freeze Ruby: Giải Thích Tính Thay Đổi Đối Tượng

Vấn đề xuất hiện nếu xử lý other_name như bản sao chuỗi.

other_name[0] = 'T'
name # "Teter"
other_name # "Teter"

Vì cả hai biến trỏ đến cùng chuỗi, vừa đổi "Peter" thành "Teter". Đó là vấn đề vì có lẽ muốn giữ "Peter".

Sao Chép Đối Tượng

Cách xử lý vấn đề này dùng phương thức dup.

Nói với Ruby cho bản sao đối tượng. Cũng có phương thức clone, ngoài sao chép đối tượng, còn sao chép trạng thái frozen & các singleton method định nghĩa trên đối tượng.

Ví dụ:

numbers = [1, 2, 3]
more_numbers = numbers.dup
more_numbers << 4
numbers      # [1, 2, 3]
more_numbers # [1, 2, 3, 4]

Trong ví dụ, mảng numbers gốc không thay đổi. Thử bỏ gọi dup dòng 3 xem sao 🙂

Phương Thức Freeze Ruby

Cách khác giữ đối tượng an toàn khỏi thay đổi không mong muốn là 'đóng băng' (freeze). Bất kỳ đối tượng Ruby nào có thể đóng băng bằng phương thức freeze.

Khi đối tượng bị đóng băng, mọi cố gắng thay đổi sẽ ném exception RuntimeError.

Lưu ý: Dùng phương thức frozen? kiểm tra đối tượng đã đóng băng chưa.

Ví dụ:

animals = %w( cat dog tiger )
animals.freeze
animals << 'monkey' # RuntimeError: can't modify frozen Array

Lưu ý một điều: chỉ đóng băng một đối tượng, ví dụ mảng, ngăn thêm/bỏ phần tử. Nhưng chuỗi bên trong mảng không bị đóng băng, vẫn có thể thay đổi!

animals[1][0] = 't' # => ["cat", "tog", "tiger"]

Muốn đóng băng chuỗi cần gọi freeze trên chúng. Như: animals.each(&:freeze).

Chuỗi Đóng Băng (Frozen Strings)

Đối tượng thay đổi cũng ảnh hưởng hiệu năng, đặc biệt chuỗi. Lý do là trong chương trình lớn, cùng một chuỗi có thể dùng nhiều lần.

Ruby tạo đối tượng mới cho mỗi chuỗi, dù hai chuỗi trông giống nhau, hay nói cách khác, cùng 'nội dung'. Dễ thấy trong irb dùng object_id.

Ví dụ:

a = 'test'
b = 'test'
a.object_id # 76325640
b.object_id # 76317550

Đây là vấn đề vì tiêu tốn bộ nhớ và chu kỳ CPU thêm.

Từ Ruby 2.1, khi dùng frozen strings, Ruby dùng cùng đối tượng chuỗi. Tránh tạo bản sao mới của cùng chuỗi. Kết quả tiết kiệm bộ nhớ và tăng hiệu năng nhỏ.

Rails dùng rộng rãi frozen strings vì lý do này. Xem PR này.

Điều này thúc đẩy team phát triển Ruby xem xét chuyển chuỗi thành đối tượng bất biến mặc định. Fakt, Ruby 2.3, mới phát hành vài ngày trước, có hai cách bật cho dự án.

Cách một: include # frozen_string_literal: true đầu mỗi tệp muốn chuỗi bất biến. Cách hai: đối số dòng lệnh --enable=frozen-string-literal.

Chuỗi bất biến mặc định có thể đến Ruby 3.0.

Đừng quá đà đóng băng tất cả chuỗi trong app. Chỉ làm cho chuỗi dùng trăm lần để thấy lợi ích. Nói vậy, đây là công cụ tìm chuỗi tiềm năng đóng băng.

Biết Phương Thức Của Bạn

Không phải tất cả phương thức trong đối tượng thay đổi thực sự thay đổi đối tượng, ví dụ gsub trả về chuỗi mới, để nguyên gốc.

Một số phương thức có phiên bản thay thế thay đổi đối tượng ngay chỗ (in-place), thường hiệu quả hơn. Các phương thức này thường kết thúc bằng dấu chấm than ! chỉ hiệu ứng.

Hai ví dụ 'bang' method: gsub! và map!.

Lưu ý:

Phương thức kết thúc ! không luôn có nghĩa 'phương thức thay đổi đối tượng'.

Nói chung, ký hiệu ! dùng chỉ 'nguy hiểm'. Ví dụ ví dụ là exit!, thoát chương trình ngay lập tức, bỏ qua handler thoát.

Cũng có phương thức thay đổi đối tượng không kết thúc !. Ví dụ: delete, clear, push, concat, và nhiều hơn.

Tổng Kết

Tính thay đổi chủ đề棘手, nhưng đọc bài này bạn chuẩn bị tốt hơn xử lý nó. Kiểm tra tài liệu Ruby nếu không chắc phương thức làm gì, giúp tránh vấn đề.

Hy vọng bài viết hữu ích, xin vui lòng chia sẻ với bạn bè để họ tận hưởng cũng như. Cũng tham gia bản tin dưới để không bỏ lỡ nội dung tương tự!