2015-12-30 17 views
7

Załóżmy, że mam klasy:Ruby dodać metodę do klasy

class Foo 
end 

dodać metodę do tej klasy Znam 2 opcje:

  1. znoszące klasę i wdrożenie metody:

    class Foo 
        def bar 
        end 
    end 
    
  2. pomocą class_eval do realizacji sposobu:

    Foo.class_eval { def bar; end} 
    

Jaka jest różnica? Który jest lepszy?

+0

['Foo # define_method'] (http://ruby-doc.org/core-2.2.0/Module.html#method-i-define_method) również. – mudasobwa

Odpowiedz

15

W rzeczywistości istnieje kilka innych sposobów dodawania nowych metod do klasy. Można na przykład zdefiniować metody w module i połączyć moduł z oryginalną klasą.

module ExtraMethods 
    def bar 
    end 
end 

Foo.class_eval { include ExtraMethods } 
class Foo 
    include ExtraMethods 
end 

Nie ma nic lepszego ani gorszego. Dwa (lub trzy) sposoby, o których wspomniałeś, mają inne zachowanie i możesz chcieć użyć jednego lub drugiego w zależności od potrzeb (lub preferencji). W większości przypadków jest to subiektywne. W innych przypadkach zależy to od struktury kodu.

Główna różnica między ponownym otwarciem klasy a używaniem class_eval polega na tym, że pierwsza z nich jest również definicją klasy, podczas gdy druga wymaga już zdefiniowania oryginalnej klasy.

W niektórych przypadkach ponowne otwarcie klasy może spowodować nieoczekiwane efekty uboczne. Załóżmy, że w pliku zdefiniowano kilka metod. Następnie ponownie otwórz Foo w config/initializers/extra.rb i dodajesz metodę bar.

Korzystasz z , gdy używasz Foo, ale zamiast ręcznie wymagać lib/foo.rb, polegasz na funkcji automatycznego ładowania.

Jeśli extra.rb jest ładowany przed lib/foo.rb, co może się zdarzyć, że klasa Foo jest już zdefiniowany w swoim otoczeniu, a kod nie będzie ładować lib/foo.rb. To, co będziesz miał, to klasa Foo zawierająca tylko zdefiniowane rozszerzenie bar, a nie oryginalna wersja Foo.

Innymi słowy, jeśli z jakiegoś powodu ponownie otworzysz klasę, aby dodać niektóre metody bez upewnienia się, że pełna definicja klasy origina została załadowana jako pierwsza (lub później), kod może się zepsuć, jeśli opiera się na automatycznym ładowaniu.

Odwrotnie, Foo.class_eval wywołuje metodę na , dlatego oczekuje, że oryginalna definicja Foo będzie już obecna w momencie próby dodania nowych metod. Dzięki temu po dodaniu nowych metod klasa Foo zostanie już zdefiniowana.

Podsumowując, główna różnica polega na tym, że ponowne otwarcie klasy pozwala (na dobre i na złe) dodawać metody do klasy, która nie została jeszcze załadowana, podczas gdy klasa class_eval wymaga, aby klasa była już zdefiniowana.

Ogólnie rzecz biorąc, jeśli nie definiuję podklas o podanej nazwie lub klasach ponownego otwierania, mam pełną kontrolę nad tym, wolę drugie podejście, ponieważ w dużych bazach kodu zachowuje on kod bardziej konserwowalny. W rzeczywistości generalnie używam mixinów, jeśli rozszerzam klasy firm trzecich, aby móc zachować łańcuch przodków pełnej metody, jeśli chcę zastąpić istniejące metody.

+0

Nie ma "autoloadki Ruby", prawdopodobnie chodziło o "Automatyczne ładowanie Railsów". – mudasobwa

+0

@mudasobwa technicznie jest (metoda 'autoload'), ale nie miałoby to zastosowania w tym przypadku. Wprowadziłem niewielką zmianę w odpowiedzi. Dziękuję za wskazanie. –

5

Drugie podejście jest bardzo przydatne, gdy potrzebujesz czegoś dynamicznego. Ruby ma w rzeczywistości kilka zakresów:

# scope one, opened with `class` keyword 
class ... 
    # scope two, opened with `def` keyword 
    def ... 
    end 
end 

Dzięki class_eval można udostępniać zakresy.

>> foo = 1 
=> 1 
>> class Foo 
>> puts foo 
>> def bar 
>>  puts foo 
>> end 
>> end 
NameError: undefined local variable or method 'foo' for Foo:Class 
     from (irb):3:in <class:Foo> 
     from (irb):2 
>> Foo 
=> Foo 
>> Foo.class_eval { 
?> puts foo 
>> define_method :bar do 
>>  puts foo 
>> end 
>> } 
1 
=> :bar 
>> Foo.new.bar 
1 
+1

Oznacza to jednak, że ten współużytkowany zakres nie zostanie zbuforowany podczas gdy klasa istnieje – Vasfed

Powiązane problemy