Ruby - динамическое переключение между протоколами (с модулями) - PullRequest
0 голосов
/ 29 апреля 2010

Я бы хотел, чтобы объект person (экземпляр класса Person ) говорил на языке (который представляет собой набор открытых методов, хранящихся в Language модуль):

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

По вашему мнению, какой должна быть лучшая практика для этого?

Любые комментарии и сообщения приветствуются. Спасибо.

Привет

Ответы [ 2 ]

0 голосов
/ 29 апреля 2010

Просто чтобы показать, что вы могли бы сделать это (не то, что вы должны):

include "A::B::C".split("::").inject{|p,c| (p.class == String ? Kernel.const_get(p) : p).const_get(c)}
0 голосов
/ 29 апреля 2010

Вы можете сделать это довольно элегантно в 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)"

По сути, все, что мы здесь делаем, - это создание класса Proxy для пересылки неизвестных сообщений Person в языковой модуль, хранящийся в переменной экземпляра Person s @current_language. «Уловка», которую я здесь использовал, заключается в создании hello, bye и т. Д. module методов, а не методов экземпляра. Затем я назначил фактический модуль в @current_language.

Здесь вы также заметите, что переменная экземпляра @current_language из Person недоступна в модулях 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)"
...