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