2010-04-29 24 views
0

我想允許對象(從類實例化)講一種語言(這是存儲在語言模塊的公共方法的集合):紅寶石 - 協議(含模塊)之間進行動態切換

class Person 
    attr_accessor :current_language 

    def quit 
    # Unselect the current language, if any: 
    @current_language = nil 
    end 
end 

假設語言如下:使用的

module Language 
    module Japanese 
    def konnichiwa 
     "こんにちは! (from #{@current_language} instance variable)" 
    end 

    def sayounara 
     "さようなら。" 
    end 
    end 

    module French 
    def bonjour 
     "Bonjour ! (from #{@current_language} instance variable)" 
    end 

    def au_revoir 
     "Au revoir." 
    end 
    end 

    module English 
    def hello 
     "Hello! (from #{@current_language} instance variable)" 
    end 

    def bye 
     "Bye." 
    end 
    end 
end 

示例:

person = Person.new 

person.current_language # => nil 
person.hello   # => may raise a nice no method error 

person.current_language = :english 
person.hello # => "Hello! (from english instance variable)" 
person.bonjour # => may also raise a no method error 
person.quit 

person.current_language = :french 
person.bonjour # => "Bonjour ! (from french instance variable)" 

正如你所看到的,一種語言就是一種協議。所以一個人可以切換一個特定的協議,但一次只能切換一個。

由於模塊化的原因,將每種語言存儲到模塊中都很友善。所以我認爲這種方式是更合乎邏輯的Ruby方式,不是。

但是,我認爲,這是不可能寫出這樣的事:

class Person 
    include "Language::#{@current_language}" unless @current_language.nil? 
end 

根據你,你應該是這樣做的最佳做法是什麼?

歡迎任何評論和消息。謝謝。

問候

回答

0

您可以在Ruby中,如果你正確地安排你的模塊做到這一點非常典雅。

module LanguageProxy 
    def method_missing(phrase) 
    if @current_language.respond_to?(phrase) 
     @current_language.send(phrase) 
    else 
     super 
    end 
    end 
end 

module Language 
    module French 
    def self.bonjour 
     "Bonjour ! (from #{@current_language} instance variable)" 
    end 

    def self.au_revoir 
     "Au revoir." 
    end 
    end 

    module English 
    def self.hello 
     "Hello! (from #{@current_language} instance variable)" 
    end 

    def self.bye 
     "Bye." 
    end 
    end 
end 

class Person 
    attr_accessor :current_language 

    include LanguageProxy 

    def quit 
    @current_language = nil 
    end 
end 

person = Person.new 

person.current_language # => nil 
begin 
    p person.hello   # => may raise a nice no method error 
rescue 
    puts "Don't know hello" 
end 

person.current_language = Language::English 
p person.hello # => "Hello! (from english instance variable)" 
begin 
    p person.bonjour # => may also raise a no method error 
rescue 
    puts "Don't know bonjour" 
end 
person.quit 

person.current_language = Language::French 
p person.bonjour # => "Bonjour ! (from french instance variable)" 

從本質上講,所有我們在這裏做的是創建一個代理類Person的來歷不明的郵件存儲在Person語言模塊「向S @current_language實例變量。我在這裏使用的「技巧」是製作hello,bye模塊方法,而不是實例方法。然後,我將實際模塊分配到@current_language

您還會注意到,在Language模塊中,Person@current_language實例變量不可用。如果您需要語言方法來訪問這些變量,它會變得更棘手:快速修復可能只是將它們作爲參數傳遞。

如果你真的想用符號來表示語言,你必須用Language.const_get來做一點小小的魔法。

輸出:

 
C:\temp\ruby>ruby person.rb 
Don't know hello 
"Hello! (from instance variable)" 
Don't know bonjour 
"Bonjour ! (from instance variable)" 
+0

謝謝你馬克對這個技巧。那工作得很好!然而,我想這個模塊的方法被動態地(有一種動態包含)合併到一個對象中,然後(例如)用person.public_send()訪問它們。因此,實例變量可以從新方法訪問。 – 2010-04-29 18:50:17

0

只是爲了顯示你可以做到這一點(不是你應該):

include "A::B::C".split("::").inject{|p,c| (p.class == String ? Kernel.const_get(p) : p).const_get(c)} 
+0

感謝您的回答。但我不太瞭解你的代碼。 – 2010-04-29 21:27:51