Computer >> Máy Tính >  >> Lập trình >> Ruby

4 mẫu ghi nhớ đơn giản trong Ruby (Và một viên ngọc)

Ghi nhớ là một kỹ thuật bạn có thể sử dụng để tăng tốc các phương thức truy cập của mình. Nó lưu trữ kết quả của các phương pháp thực hiện công việc tốn thời gian, công việc chỉ cần thực hiện một lần. Trong Rails, bạn thấy ghi nhớ được sử dụng thường xuyên đến mức nó thậm chí còn bao gồm một mô-đun sẽ cung cấp các phương thức ghi nhớ cho bạn.

Sau đó, điều này đã bị loại bỏ gây tranh cãi vì chỉ sử dụng mẫu ghi nhớ thực sự phổ biến mà tôi sẽ nói đến đầu tiên. Nhưng như bạn sẽ thấy, có một số nơi mà kiểu cơ bản này không hoạt động đúng. Vì vậy, chúng ta cũng sẽ xem xét các mẫu ghi nhớ nâng cao hơn và tìm hiểu một số điều cơ bản về Ruby trong quá trình này!

Ghi nhớ siêu cơ bản

Bạn sẽ thấy mẫu ghi nhớ này mọi lúc trong Ruby:

app / models / order.rb
class User < ActiveRecord::Base
  def twitter_followers
    # assuming twitter_user.followers makes a network call
    @twitter_followers ||= twitter_user.followers
  end
end

||= ít nhiều dịch thành @twitter_followers = @twitter_followers || twitter_user.followers . Điều đó có nghĩa là bạn sẽ chỉ thực hiện cuộc gọi mạng trong lần đầu tiên bạn gọi twitter_followers và các lệnh gọi trong tương lai sẽ chỉ trả về giá trị của biến cá thể @twitter_followers .

Ghi nhớ nhiều dòng

Đôi khi, mã chậm sẽ không vừa trên một dòng mà không gây ra những điều tồi tệ cho nó. Có một số cách để mở rộng mẫu cơ bản để hoạt động với nhiều dòng mã, nhưng đây là cách tôi thích nhất:

app / models / order.rb
class User < ActiveRecord::Base
  def main_address
    @main_address ||= begin
      maybe_main_address = home_address if prefers_home_address?
      maybe_main_address = work_address unless maybe_main_address
      maybe_main_address = addresses.first unless maybe_main_address
    end
  end
end

begin...end tạo một khối mã trong Ruby có thể được coi là một thứ duy nhất, giống như {...} trong ngôn ngữ kiểu C. Đó là lý do tại sao ||= hoạt động tốt ở đây như nó đã làm trước đây.

Còn nil thì sao?

Nhưng những mẫu ghi nhớ này có một vấn đề khó chịu, tiềm ẩn. Trong ví dụ đầu tiên, điều gì sẽ xảy ra nếu người dùng không có tài khoản twitter và API người theo dõi twitter trả về nil ? Trong phần thứ hai, điều gì sẽ xảy ra nếu người dùng không có bất kỳ địa chỉ nào và khối trả về nil ?

Mỗi lần chúng tôi gọi phương thức, biến phiên bản sẽ là nil và chúng tôi sẽ thực hiện lại các lần tìm nạp đắt tiền.

Vì vậy, ||= có lẽ không phải là cách thích hợp để đi. Thay vào đó, chúng ta phải phân biệt giữa nilundefined :

app / models / order.rb
class User < ActiveRecord::Base
  def twitter_followers
    return @twitter_followers if defined? @twitter_followers
    @twitter_followers = twitter_user.followers
  end
end
app / models / order.rb
class User < ActiveRecord::Base
  def main_address
    return @main_address if defined? @main_address
    @main_address = begin
      main_address = home_address if prefers_home_address?
      main_address ||= work_address
      main_address ||= addresses.first # some semi-sensible default
    end
  end
end

Thật không may, điều này xấu hơn một chút, nhưng nó hoạt động với nil , false , và mọi thứ khác. (Để xử lý nil trường hợp, bạn cũng có thể sử dụng Null Objects và các mảng trống để tránh vấn đề này. Thêm một lý do để tránh nil !)

Còn các thông số thì sao?

Chúng tôi có một số mẫu ghi nhớ hoạt động tốt cho những người truy cập đơn giản. Nhưng nếu bạn muốn ghi nhớ một phương thức có tham số, như phương thức này thì sao?

app / models / city.rb
class City < ActiveRecord::Base
  def self.top_cities(order_by)
    where(top_city: true).order(order_by).to_a
  end
end

Hóa ra là Hash của Ruby có một công cụ sinh dục hoạt động hoàn hảo cho tình huống này. Bạn có thể gọi Hash.new với một khối:

Hash.new {|h, key| h[key] = some_calculated_value }

Sau đó, mỗi khi bạn cố gắng truy cập vào một khóa trong hàm băm chưa được đặt, nó sẽ thực thi khối. Và nó sẽ chuyển chính hàm băm cùng với khóa mà bạn đã cố gắng truy cập vào khối.

Vì vậy, nếu bạn muốn ghi nhớ phương thức này, bạn có thể thực hiện một số việc như:

app / models / city.rb
class City < ActiveRecord::Base
  def self.top_cities(order_by)
    @top_cities ||= Hash.new do |h, key|
      h[key] = where(top_city: true).order(key).to_a
    end
    @top_cities[order_by]
  end
end

Và bất kể bạn chuyển gì vào order_by , kết quả chính xác sẽ được ghi nhớ. Vì khối chỉ được gọi khi khóa không tồn tại, bạn không phải lo lắng về kết quả khối là nil hoặc false.

Thật tuyệt vời, Hash hoạt động tốt với các khóa thực sự là mảng:

h = {}
h[["a", "b"]] = "c"
h[["a", "b"]] # => "c"

Vì vậy, bạn có thể sử dụng mẫu này trong các phương thức với bất kỳ số lượng tham số nào!

Tại sao lại phải trải qua những rắc rối này?

Tất nhiên, nếu bạn bắt đầu thêm các mẫu ghi nhớ này vào nhiều phương thức, mã của bạn sẽ trở nên khó đọc khá nhanh. Các phương pháp của bạn sẽ là tất cả các nghi lễ và không có thực chất.

Vì vậy, nếu bạn đang làm việc trên một ứng dụng cần nhiều bản ghi nhớ, bạn có thể muốn sử dụng một viên ngọc có khả năng xử lý bản ghi nhớ cho bạn bằng một API thân thiện và đẹp mắt. Memoist có vẻ là một cái hay, và khá giống với những gì Rails từng có. (Hoặc, với kiến ​​thức ghi nhớ mới tìm thấy của bạn, bạn thậm chí có thể thử tự xây dựng một kiến ​​thức).

Nhưng luôn thú vị khi điều tra các mẫu như thế này, xem cách chúng được kết hợp với nhau, vị trí hoạt động và vị trí của các cạnh sắc nét. Và bạn có thể tìm hiểu một số điều thú vị về một số tính năng ít được biết đến của Ruby trong khi khám phá.