2008-10-16 7 views
5

Hy vọng rằng tôi đã không hiểu lầm ý nghĩa của "gõ vịt", nhưng từ những gì tôi đã đọc, nó có nghĩa là tôi nên viết mã dựa trên cách đối tượng phản ứng với các phương thức hơn là loại/lớp nó là gì.Tôi có thể cải thiện phương pháp này bằng cách gõ vịt không?

Dưới đây là các mã:

def convert_hash(hash) 
    if hash.keys.all? { |k| k.is_a?(Integer) } 
    return hash 
    elsif hash.keys.all? { |k| k.is_a?(Property) } 
    new_hash = {} 
    hash.each_pair {|k,v| new_hash[k.id] = v} 
    return new_hash 
    else 
    raise "Custom attribute keys should be ID's or Property objects" 
    end 
end 

Những gì tôi muốn là để đảm bảo rằng tôi kết thúc với một hash nơi các phím là một số nguyên đại diện cho ID của một đối tượng ActiveRecord. Tôi đặc biệt không thích phải lặp lại thông qua các khóa băm hai lần với số all? để xác định xem tôi có cần lấy ID ra không.

Tất nhiên, tôi sẽ chấp nhận bất kỳ lời đề nghị khác để cải thiện mã này cũng :)

+0

thậm chí Chưa bao giờ nghe nói về "gõ vịt" trước. Bạn đã vượt qua điều đó ở đâu? –

+0

@Brian, http://en.wikipedia.org/wiki/Duck_typing –

Trả lời

11

Làm thế nào bạn viết phương pháp này nên phụ thuộc vào việc bạn mong đợi một ngoại lệ được ném trong quá trình thực hiện chương trình bình thường. Nếu bạn muốn một thông báo ngoại lệ có thể đọc được vì người dùng cuối có thể thấy nó, thì hãy ném một cách thủ công có ý nghĩa. Nếu không, tôi chỉ làm như sau:

def convert(hash) 
    new_hash = {} 
    hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v } 
    return new_hash 
end 

Điều này sẽ thực hiện chính xác điều tương tự và bạn vẫn sẽ nhận được ngoại lệ nếu khóa mảng không có trường id. Thậm chí tốt hơn, điều này sử dụng một chút gõ vịt bởi vì bây giờ bất cứ điều gì có một lĩnh vực id sẽ được chấp nhận, đó là tốt hơn so với kiểm tra một cách rõ ràng cho một cái gì đó là một tài sản. Điều này làm cho mã của bạn linh hoạt hơn, đặc biệt khi thử nghiệm đơn vị.

Chúng tôi vẫn có một kiểm tra rõ ràng đối với các đối tượng số nguyên, nhưng loại trường hợp đặc biệt này thường được chấp nhận, đặc biệt khi kiểm tra các kiểu dữ liệu tích hợp.

+1

Câu trả lời tuyệt vời, mô tả với một số mã Rubyish, Eli. Cám ơn rất nhiều vì câu trả lời. –

+1

Vấn đề với điều đó là đối tượng MỌI trong Ruby có một phương thứC#id. Nó được định nghĩa trên Object, và đưa ra một tham chiếu duy nhất trong trình thông dịch Ruby cho đối tượng đó. Nó không được chấp nhận mặc dù, vì vậy trong khi bạn sẽ nhận được một cảnh báo, bạn sẽ không nhận được một ngoại lệ. – madlep

3

Nhập liệu thực sự là một phiên bản đa sắc thái của đa hình. Trong một ngôn ngữ gõ tĩnh như Java, bạn phải tạo một giao diện rõ ràng cho trình biên dịch tất cả các phương thức mà một biến cụ thể có thể chấp nhận. Với một ngôn ngữ động như Ruby, các giao diện vẫn tồn tại theo nghĩa trừu tượng, chúng chỉ là ngầm.

Sự cố là bạn đang chấp nhận hai cấu trúc dữ liệu khác nhau thành một phương pháp. Cách thực hiện thao tác gõ vịt là yêu cầu tất cả các đối tượng được truyền cho phương thức của bạn tuân theo cùng một hợp đồng (nghĩa là luôn luôn là một băm của số nguyên cho đối tượng [Foo].) Quá trình chuyển đổi băm bằng các khóa thuộc tính thành cấu trúc đúng phải là công việc của mã khách hàng. Điều đó có thể được thực hiện rất dễ dàng với một lớp trình bao bọc đơn giản hoặc một hàm chuyển đổi chỉ bao gồm phần thân của mệnh đề elseif của bạn.

Điểm mấu chốt là tùy thuộc vào người gọi phương thức này để đảm bảo các thông số của anh ta tất cả đều theo cách mà phương pháp của bạn mong đợi họ quack. Nếu họ không, anh ta là người cần tìm ra cách làm cho con gà tây của mình quack như một con vịt, không phải bạn.

0

Điều tôi muốn là đảm bảo rằng tôi kết thúc bằng hàm băm trong đó các khóa là số nguyên biểu thị ID của đối tượng ActiveRecord.

Bạn có thể nên kiểm tra điều đó khi bạn đang tạo/chèn vào băm. Bạn có thể thử một cái gì đó như thế này:

 
h = {} 
def h.put obj 
    self[obj.id]=obj 
end 

hoặc có thể

 
h = {} 
def h.[]= key, value 
    raise "hell" unless key == value.id 
    super 
end