2010-03-17 40 views
15

Tôi có một tình huống đối với Ruby, nơi một đối tượng có thể cần thiết được tạo ra, nhưng nó không chắc chắn. Và như việc tạo ra các đối tượng có thể tốn kém tôi không quá háo hức tạo ra nó. Tôi nghĩ đây là một trường hợp rõ ràng cho việc tải chậm. Làm thế nào tôi có thể xác định một đối tượng mà không được tạo ra chỉ khi ai đó gửi một tin nhắn đến nó? Đối tượng sẽ được tạo trong một khối. Có cách nào để tải/khởi tạo đơn giản trong Ruby? Có phải những thứ này được hỗ trợ bởi một số đá quý, cung cấp các giải pháp khác nhau cho các trường hợp khởi tạo lười biếng của các đối tượng khác nhau không? Cảm ơn lời đề nghị của bạn!Đánh giá lười biếng trong Ruby

+0

Thay vì tự lăn, bạn có thể sử dụng [lazy.rb] (https://github.com/mental/lazy). Có một số ví dụ về cách sử dụng trong sách [Thực hành tốt nhất của Ruby] (http://oreilly.com/catalog/9780596523015/), xem trang 123 và chuyển tiếp. –

Trả lời

31

Có hai cách.

Đầu tiên là để người gọi gọi xử lý việc tạo đối tượng lười biếng. Đây là giải pháp đơn giản nhất, và nó là mẫu rất phổ biến trong mã Ruby.

class ExpensiveObject 
    def initialize 
    # Expensive stuff here. 
    end 
end 

class Caller 
    def some_method 
    my_object.do_something 
    end 

    def my_object 
    # Expensive object is created when my_object is called. Subsequent calls 
    # will return the same object. 
    @my_object ||= ExpensiveObject.new 
    end 
end 

Lựa chọn thứ hai là để cho các đối tượng khởi bản thân lười biếng. Chúng tôi tạo ra một đối tượng đại biểu xung quanh đối tượng thực tế của chúng tôi để đạt được điều này. Cách tiếp cận này phức tạp hơn một chút và không được khuyến nghị trừ khi bạn có mã gọi hiện tại mà bạn không thể sửa đổi, ví dụ.

class ExpensiveObject  # Delegate 
    class RealExpensiveObject # Actual object 
    def initialize 
     # Expensive stuff here. 
    end 

    # More methods... 
    end 

    def initialize(*args) 
    @init_args = args 
    end 

    def method_missing(method, *args) 
    # Delegate to expensive object. __object method will create the expensive 
    # object if necessary. 
    __object__.send(method, *args) 
    end 

    def __object__ 
    @object ||= RealExpensiveObject.new(*@init_args) 
    end 
end 

# This will only create the wrapper object (cheap). 
obj = ExpensiveObject.new 

# Only when the first message is sent will the internal object be initialised. 
obj.do_something 

Bạn cũng có thể sử dụng stdlib delegate để tạo điều này trên đầu trang.

+0

Trong ví dụ đầu tiên, tôi cần giữ nguyên thể hiện của lớp Caller. Đúng? Nhưng sự khác biệt đối với tôi là gì - để giữ cho cá thể lớp Caller hoặc giữ cá thể lớp đắt tiền? – demas

+0

Trong ví dụ đầu tiên, lớp 'Caller' chỉ là một ví dụ về cách bạn sẽ sử dụng * lớp ExpensiveObject. Sự khác biệt: giới thiệu sự lười biếng nơi bạn * sử dụng * 'ExpensiveObject' (đơn giản), hoặc giới thiệu sự lười biếng trong chính' ExpensiveObject' * (hơi phức tạp hơn). – molf

+1

@molf: Bất cứ khi nào bạn ghi đè 'method_missing' bạn * phải * cũng ghi đè' respond_to? '(Hoặc tốt hơn là' respond_to_missing? 'Trong 1.9.2). Xem http://blog.marc-andre.ca/2010/11/methodmissing-politely.html – Nemo157

5

Nếu bạn muốn lười biếng đánh giá từng phần mã, sử dụng proxy:

class LazyProxy 

    # blank slate... (use BasicObject in Ruby 1.9) 
    instance_methods.each do |method| 
    undef_method(method) unless method =~ /^__/ 
    end 

    def initialize(&lazy_proxy_block) 
    @lazy_proxy_block = lazy_proxy_block 
    end 

    def method_missing(method, *args, &block) 
    @lazy_proxy_obj ||= @lazy_proxy_block.call # evaluate the real receiver 
    @lazy_proxy_obj.send(method, *args, &block) # delegate unknown methods to the real receiver 
    end 
end 

Sau đó bạn sử dụng nó như thế này:

expensive_object = LazyProxy.new { ExpensiveObject.new } 
expensive_object.do_something 

Bạn có thể sử dụng mã này để làm khởi tùy tiện phức tạp của công cụ đắt tiền:

expensive_object = LazyProxy.new do 
    expensive_helper = ExpensiveHelper.new 
    do_really_expensive_stuff_with(expensive_helper) 
    ExpensiveObject.new(:using => expensive_helper) 
end 
expensive_object.do_something 

Cách hoạt động? Bạn khởi tạo một đối tượng LazyProxy chứa các chỉ dẫn về cách xây dựng một số đối tượng đắt tiền trong một Proc. Nếu sau đó bạn gọi một số phương thức trên đối tượng proxy, nó sẽ khởi tạo đối tượng đắt tiền đầu tiên và sau đó ủy quyền cuộc gọi phương thức cho đối tượng đó.

Các vấn đề liên quan