2010-05-19 25 views
21

Trong Ruby, các đối tượng có một phương pháp tiện dụng gọi method_missing cho phép một để xử lý các cuộc gọi phương pháp cho phương pháp đã thậm chí không được (rõ ràng) định nghĩa:Có tương đương với method_missing của Ruby bằng các ngôn ngữ khác không?

Được triệu gọi bởi Ruby khi obj được gửi một thông điệp nó không thể xử lý. biểu tượng là biểu tượng cho phương thức được gọi, và arg là bất kỳ đối số nào được truyền cho nó. Theo mặc định, thông dịch viên sẽ phát sinh lỗi khi phương thức này được gọi. Tuy nhiên, có thể ghi đè lên phương pháp để cung cấp hành vi năng động hơn. Ví dụ dưới đây tạo ra một lớp Roman, trả lời các phương thức có các tên gồm các chữ số La Mã, trả về các giá trị số nguyên tương ứng.

class Roman 
def romanToInt(str) 
    # ... 
end 
def method_missing(methId) 
    str = methId.id2name 
    romanToInt(str) 
end 
end 

r = Roman.new 
r.iv  #=> 4 
r.xxiii #=> 23 
r.mm  #=> 2000 

Ví dụ, Ruby on Rails sử dụng này để cho phép các cuộc gọi đến các phương pháp như find_by_my_column_name.

Câu hỏi của tôi là, ngôn ngữ nào khác hỗ trợ tương đương với method_missing và cách bạn triển khai mã tương đương trong mã của mình?

Trả lời

11

Một số trường hợp sử dụng method_missing có thể được triển khai bằng Python sử dụng __getattr__ ví dụ:

class Roman(object): 
    def roman_to_int(self, roman): 
    # implementation here 

    def __getattr__(self, name): 
    return self.roman_to_int(name) 

Sau đó, bạn có thể làm:

>>> r = Roman() 
>>> r.iv 
4 
+0

'roman_to_int' có triển khai trống không, vậy tại sao r.iv trả lại 4? – Tim

+0

@Tim Giả sử bạn điền vào chỗ trống bằng mã sẽ trả về một giá trị. Anh ta quyết định không hiển thị tất cả các chi tiết. Điều này có thể được thực hiện với một số La Mã để chuyển đổi số. –

8

Objective-C hỗ trợ điều tương tự và gọi nó là forwarding.

12

Đối tượng PHP có thể bị quá tải với phương thức đặc biệt __call.

Ví dụ:

<?php 
class MethodTest { 
    public function __call($name, $arguments) { 
     // Note: value of $name is case sensitive. 
     echo "Calling object method '$name' " 
      . implode(', ', $arguments). "\n"; 
    } 
} 

$obj = new MethodTest; 
$obj->runTest('in object context'); 
?> 
2

Actionscript 3.0 có một lớp Proxy có thể được mở rộng để cung cấp chức năng này.

dynamic class MyProxy extends Proxy { 
    flash_proxy override function callProperty(name:*, ...rest):* { 
    try { 
     // custom code here 
    } 
    catch (e:Error) { 
     // respond to error here 
    } 
} 
15

Smalltalk có thông báo doesNotUnderstand, có lẽ là triển khai ban đầu của ý tưởng này, vì Smalltalk là một trong những cha mẹ của Ruby. Việc triển khai mặc định sẽ hiển thị một cửa sổ lỗi, nhưng nó có thể được ghi đè để làm điều gì đó thú vị hơn.

+0

Cool! Nhưng có ví dụ nào về cách bạn sử dụng thông báo này không? –

+1

"Kết hợp với #become :, gây ra hai đối tượng để giao dịch địa điểm trong bộ nhớ, khả năng này của #doesNotUnderstand: rất hữu ích để triển khai ProxyObject chẳng hạn như cần thiết trong khung công tác Ánh xạ đối tượng" http://c2.com/cgi/ wiki? DoesNotUnderstand –

5

Trong Common Lisp, no-applicable-method có thể được sử dụng cho mục đích này, theo Common Lisp Hyper Spec:

Chức năng chung không áp dụng-phương pháp được gọi khi hàm chung được gọi và không có phương thức nào trên hàm chung đó được áp dụng. Phương thức mặc định báo hiệu lỗi.

Chức năng chung không áp dụng-phương pháp không có ý định được gọi bởi các lập trình viên. Các lập trình viên có thể viết các phương thức cho nó.

Vì vậy, ví dụ:

(defmethod no-applicable-method (gf &rest args) 
    ;(error "No applicable method for args:~% ~s~% to ~s" args gf) 
    (%error (make-condition 'no-applicable-method :generic-function gf :arguments args) '() 
     ;; Go past the anonymous frame to the frame for the caller of the generic function 
     (parent-frame (%get-frame-ptr)))) 
+2

Tôi không nghĩ rằng điều này mang lại cho bạn cùng một loại cú pháp cú pháp bạn nhận được từ nó trong Ruby, mặc dù. Trong ví dụ 'r.xxiii', để gọi NO-APPLICABLE-METHOD từ (XXIII R), trước tiên bạn cần định nghĩa một hàm generic có tên XXIII, đúng không? –

2

Tcl có một cái gì đó tương tự. Bất cứ khi nào bạn gọi bất kỳ lệnh nào không thể tìm thấy, thủ tục unknown sẽ được gọi. Mặc dù nó không phải là thứ bạn thường sử dụng, đôi khi nó có thể hữu ích.

8

Perl có AUTOLOAD hoạt động trên các chương trình con/phương thức đối tượng lớp học &.

dụ chương trình con:

use 5.012; 
use warnings; 

sub AUTOLOAD { 
    my $sub_missing = our $AUTOLOAD; 
    $sub_missing =~ s/.*:://; 
    uc $sub_missing; 
} 

say foo(); # => FOO 

lớp/đối tượng gọi phương thức ví dụ:

use 5.012; 
use warnings; 

{ 
    package Shout; 

    sub new { bless {}, shift } 

    sub AUTOLOAD { 
     my $method_missing = our $AUTOLOAD; 
     $method_missing =~ s/.*:://; 
     uc $method_missing; 
    } 
} 

say Shout->bar;   # => BAR 

my $shout = Shout->new; 
say $shout->baz;  # => BAZ 
2

Trong CFML (ColdFusion, Railo, OpenBD), xử lý onMissingMethod() sự kiện, được xác định trong phạm vi một thành phần, sẽ nhận được các cuộc gọi phương thức không xác định trên thành phần đó. Đối số missingMethodNamemissingMethodArguments được tự động chuyển vào, cho phép xử lý động cuộc gọi phương thức bị thiếu. Đây là cơ chế tạo điều kiện cho việc tạo ra các chương trình setter/getter ngầm định trước khi chúng bắt đầu được xây dựng vào các công cụ CFML khác nhau.

9

JavaScript có noSuchMethod, nhưng tiếc là điều này chỉ được Firefox/Spidermonkey hỗ trợ.

Dưới đây là một ví dụ:

wittyProjectName.__noSuchMethod__ = function __noSuchMethod__ (id, args) { 
    if (id == 'errorize') { 
    wittyProjectName.log("wittyProjectName.errorize has been deprecated.\n" + 
         "Use wittyProjectName.log(message, " + 
         "wittyProjectName.LOGTYPE_ERROR) instead.", 
         this.LOGTYPE_LOG); 
    // just act as a wrapper for the newer log method 
    args.push(this.LOGTYPE_ERROR); 
    this.log.apply(this, args); 
    } 
} 
1

Boo có IQuackFu - có đã là một bản tóm tắt tuyệt vời trên SO tại how-can-i-intercept-a-method-call-in-boo

Dưới đây là một ví dụ:

class XmlObject(IQuackFu): 
_element as XmlElement 

def constructor(element as XmlElement): 
    _element = element 

def QuackInvoke(name as string, args as (object)) as object: 
    pass # ignored 

def QuackSet(name as string, parameters as (object), value) as object: 
    pass # ignored 

def QuackGet(name as string, parameters as (object)) as object: 
    elements = _element.SelectNodes(name) 
    if elements is not null: 
     return XmlObject(elements[0]) if elements.Count == 1 
     return XmlObject(e) for e as XmlElement in elements 

override def ToString(): 
    return _element.InnerText 
7

tôi đang tìm kiếm điều này trước đây, và tìm thấy một useful list (nhanh chóng bị vượt qua ở đây) như là một phần của dự án Merd trên SourceForge.


Construct       Language 
-----------      ---------- 
AUTOLOAD       Perl 
AUTOSCALAR, AUTOMETH, AUTOLOAD... Perl6 
__getattr__      Python 
method_missing      Ruby 
doesNotUnderstand     Smalltalk 
__noSuchMethod__(17)    CoffeeScript, JavaScript 
unknown       Tcl 
no-applicable-method    Common Lisp 
doesNotRecognizeSelector   Objective-C 
TryInvokeMember(18)    C# 
match [name, args] { ... }   E 
the predicate fail     Prolog 
forward       Io 

Với chú thích:

  • (17) firefox
  • (18) C# 4, chỉ cho "năng động" đối tượng
5

C# bây giờ có TryInvokeMember, cho đối tượng động (kế thừa từ DynamicObject)

+4

DynamicObject thậm chí còn có một số phương pháp khác trong vấn đề này, mặc dù TryInvokeMember là tương đương trực tiếp của method_missing. Và cũng có ExpandoObject, cho phép thêm các thuộc tính trong thời gian chạy. Tùy thuộc vào những gì bạn thực sự muốn giải quyết, đây có thể là những cách tốt quá. – OregonGhost

8

Điều này được thực hiện ở Lua bằng cách đặt khóa __index của số metatable.

t = {} 
meta = {__index = function(_, idx) return function() print(idx) end end} 
setmetatable(t, meta) 

t.foo() 
t.bar() 

này mã sẽ đầu ra:

foo 
bar 
2

tương đương của nó trong Io đang sử dụng phương pháp forward.

Từ các tài liệu:

Nếu một đối tượng không đáp ứng với một tin nhắn, nó sẽ gọi "chuyển tiếp" phương pháp của nó nếu nó có một ....

Dưới đây là một ví dụ đơn giản:

Shout := Object clone do (
    forward := method (
     method_missing := call message name 
     method_missing asUppercase 
    ) 
) 

Shout baz println  # => BAZ 

/I3az/