2012-12-07 13 views
7

W poniższym kodzie metoda roar nie jest zdefiniowana w klasie Lion, ale nadal może być wywołana przy użyciu method_missing.Kiedy używać `method_missing`

class Lion 
    def method_missing(name, *args) 
    puts "Lion will #{name}: #{args[0]}" 
    end 
end 

lion = Lion.new 
lion.roar("ROAR!!!") # => Lion will roar: ROAR!!! 

W jakich sytuacjach iw jaki sposób należy korzystać z tej method_missing? I czy jest bezpieczny w użyciu?

+0

Metoda ducha, zwykle narzędzie do metaprogramowania, tak myślę. Uważaj na wydajność. Metoda Ghosta jest powolna. – halfelf

+0

Myślę, że masz na myśli "klasę Lwa" zamiast "Klasy Lwa". –

+0

@EricWalker: dzięki, wpisując błąd. – tokhi

Odpowiedz

8

Podsumowanie: Kiedy używać? Kiedy to ułatwi życie, nie będzie komplikować życia innych osób.


Oto jeden przykład, który pojawia się w umyśle. Jest to klejnot od redis_failover.

# Dispatches redis operations to master/slaves. 
def method_missing(method, *args, &block) 
    if redis_operation?(method) 
    dispatch(method, *args, &block) 
    else 
    super 
    end 
end 

Tutaj sprawdzamy, czy wywołana metoda jest w rzeczywistości poleceniem połączenia redis. Jeśli tak, delegujemy go do podstawowych połączeń. Jeśli nie, przekaź super.

Innym znanym przykładem aplikacji method_missing jest wyszukiwarka ActiveRecord.

User.find_by_email_and_age('[email protected]', 20) 

Nie ma oczywiście metody find_by_email_and_age. Zamiast tego, method_missing dzieli nazwę, analizuje części i wywołuje find z odpowiednimi parametrami.

9

To całkowicie bezpieczne do użycia pod warunkiem, że używasz go w oczekiwany sposób i nie daj się ponieść. Jednak nie wszystko, co możesz zrobić, jest warte zrobienia.

Zaletą method_missing jest to, że możesz reagować na różne rzeczy w wyjątkowy sposób.

Wada polega na tym, że nie reklamujesz swoich możliwości. Inne obiekty, które oczekują od Ciebie czegoś, nie otrzymają potwierdzenia i mogą potraktować Twój niestandardowy obiekt w sposób, który nie jest zamierzony.

Do budowania specyficznych dla domeny języków i zapewnienia bardzo luźnego kleju między komponentami, tego rodzaju rzeczy są nieocenione.

Doskonałym przykładem tego, gdzie jest to dobre dopasowanie, jest klasa Ruby OpenStruct.

+1

+1 za wzmiankę o bracie bliźniaku 'method_missing' odpowiada na' ' – akuhn

+7

W rzeczywistości brat' method_missing' jest "respond_to_missing?" Powinieneś * zawsze * zastępować oba razem. –

+0

@ JörgWMittag nigdy nie słyszałem o 'reply_to_missing' jest to klejnot !? – akuhn

4

Oto mój ulubiony

class Hash 
    def method_missing(sym,*args) 
    fetch(sym){fetch(sym.to_s){super}} 
    end 
end 

który umożliwia mi dostęp wartości hash, jak gdyby były atrybuty. Jest to szczególnie przydatne podczas pracy z danymi JSON.

Na przykład, zamiast pisać tweets.collect{|each|each['text']}, mogę po prostu napisać tweets.collect(&:text), który jest znacznie krótszy. Lub też, zamiast tweets.first['author'] mogę po prostu napisać tweets.first.author, który wydaje się bardziej naturalny. W rzeczywistości zapewnia on dostęp w stylu JavaScript do wartości skrótu.

 

NB, Czekam małpę łatanie policję do moich drzwi każdej chwili ...

+1

Można również napisać metodę, która emituje procedury, takie jak 'tweets.collect (& attr ('text'))), które byłyby następujące:' def attr (a); lambda {| e | e [a]}; end' – tadman

+0

Masz rację, ale nie mogę napisać 'tweets.first.text' (zaktualizowałem mój wpis). – akuhn

+0

Zamiast toczyć własne, dlaczego nie skorzystać z OpenStruct?Tworzenie "nagiego" obiektu jest trudne do zrobienia. Jeśli masz klucze takie jak 'hash', będzie to sprzeczne z wewnętrznymi metodami. – tadman

3

Znalazłem również blogu Paolo Perrotta (z), gdzie wykazano, kiedy używać method_missing:

class InformationDesk 
    def emergency 
    # Call emergency... 
    "emergency() called" 
    end 

    def flights 
    # Provide flight information... 
    "flights() called" 
    end 

    # ...even more methods 
end 

Sprawdź, czy usługa została zgłoszona w czasie lunchu.

class DoNotDisturb 
    def initialize 
    @desk = InformationDesk.new 
    end 

    def method_missing(name, *args) 
    unless name.to_s == "emergency" 
     hour = Time.now.hour 
     raise "Out for lunch" if hour >= 12 && hour < 14 
    end 

    @desk.send(name, *args) 
    end 
end 

# At 12:30... 
DoNotDisturb.new.emergency # => "emergency() called" 
DoNotDisturb.new.flights # ~> -:37:in `method_missing': Out for lunch (RuntimeError) 
3

Przede wszystkim trzymaj się podsumowania Sergio Tulentseva.

Niezależnie od tego, myślę, że przeglądanie przykładów jest najlepszym sposobem, aby uzyskać poczucie dobrych i złych sytuacji dla method_missing; Oto kolejny prosty przykład:


Niedawno skorzystałem z method_missing w Null Object.

  • Obiekt zerowy był zamiennikiem dla modelu zamówienia.

  • Zamówienie obejmuje różne ceny dla różnych walut.


Bez method_missing wygląda to tak:

class NullOrder 
    def price_euro 
    0.0 
    end 

    def price_usd 
    0.0 
    end 

    # ... 
    # repeat for all other currencies 
end 

Z method_missing mogę skrócić go do:

class NullOrder 
    def method_missing(m, *args, &block) 
    m.to_s =~ /price_/ ? 0.0 : super 
    end 
end 

z dodatkową korzyścią, nie mając (pamiętaj, aby) zaktualizować NullOrder po dodaniu nowych atrybutów price_xxx do Order.

Powiązane problemy