2014-10-07 51 views
0

假設一個具有以下模式:初始化期間是否可以包含模塊?

  • Foobar的類,這使得使用的邏輯的在foo或酒吧,但不是兩者,這取決於由構造給定
  • 富模塊的參數,將含有一些邏輯
  • 酒吧模塊,含有一些備選邏輯

一個很好的例子是軌道要素計算器,這可能需要半徑和速度矢量,並計算軌道要素---或備選地,可能需要眼眶l元素並計算半徑和速度矢量。

爲什麼不以更直接的方式做到這一點?嗯,我想利用實例變量,讓剛剛在時間的計算,例如,

def derived_value 
    @derived_value ||= begin 
    # compute only when/if necessary 
    end 
end 

我試着這樣做大致如下:

class Orbit 
    def initialize options = {} 
    if [:radius,:velocity].all? { |key| options.has_key?(key) } 
     # Provide instance methods which derive orbital elements from radius, velocity 
     self.include Orbit::FromRadiusAndVelocity 
    else 
     # Provide instance methods which derive radius and velocity from orbital elements 
     self.include Orbit::FromOrbitalElements 
    end 
    initialize_helper options 
    end 
end 

module Orbit::FromOrbitalElements 
    module ClassMethods 
    class << self 
     def initialize_helper options 
     @argument_of_perigee = options[:argument_of_perigee] 
     puts "From orbital elements" 
     end 

     attr_reader :argument_of_perigee 
    end 
    end 

    def self.included(base) 
    base.extend Orbit::FromOrbitalElements::ClassMethods 
    end 

    def self.radius 
    # Instance method to calculate the radius from the orbital elements 
    @radius ||= begin 
     # If radius is not already defined, calculate it from some intermediate 
     # values, which themselves depend upon orbital elements. 
    end 
    end 
end 

module Orbit::FromRadiusAndVelocity 
    module ClassMethods 
    class << self 
     def initialize_helper options 
     @radius = options[:radius] 
     @velocity = options[:velocity] 
     puts "From radius and velocity" 
     end 

     attr_reader :radius, :velocity 
    end 
    end 

    def self.included(base) 
    base.extend Orbit::FromRadiusAndVelocity::ClassMethods 
    end 

    def self.argument_of_perigee 
    # Instance method to calculate an orbital element 
    # (There would be more instance methods like this) 
    @argument_of_perigee ||= begin 
     # If argument_of_perigee is not already defined, calculate it from some intermediate 
     # values, which themselves depend upon radius and velocity. 
    end 
    end 
end 

有沒有更好的模式/是這是一個明智的用法?是否有可能做到這一點?

+1

目前尚不清楚你想做什麼。 – sawa 2014-10-07 19:23:47

+0

您不能將模塊包含到對象中,只有'Module'的實例定義此方法。但是,您可以擴展對象,但不能使用您編寫的模塊。請更新您的問題,並明確說明您要實現的目標。 – BroiSatse 2014-10-07 19:25:56

+0

現在更清楚了嗎?我不太清楚我沒有解釋得很好,但我很樂意嘗試清理那些令人困惑的部分 - 如果你能指出這些部分。 – 2014-10-07 19:38:45

回答

1

您可以通過在singleton類(有時稱爲eigenclass)中包含所選模塊,而不是對象本身。因此,初始化可以像這樣

class Orbit 
    def initialize options = {} 
    if [:radius,:velocity].all? { |key| options.has_key?(key) } 
     self.singleton_class.include Orbit::FromRadiusAndVelocity 
    else 
     self.singleton_class.include Orbit::FromOrbitalElements 
    end 

    initialize_helper options 
    end 
end 

如果你打算使用這種方法,那麼你就需要相應地調整你的模塊:

  1. 無需ClassMethod模塊,包括了所有的方法都(據說)從對象調用,而不是從一個類
  2. 取下方法名的self.(當然除了self.included

代碼的其餘部分可以是這樣的

module Orbit::FromOrbitalElements 
    def initialize_helper options 
    @argument_of_perigee = options[:argument_of_perigee] 
    puts "From orbital elements" 
    end 

    def self.included(base) 
    base.instance_eval do 
     attr_reader :argument_of_perigee 
    end 
    end 

    def radius 
    @radius ||= begin 
    end 
    end 
end 

module Orbit::FromRadiusAndVelocity 
    def self.included(base) 
    base.instance_eval do 
     attr_reader :radius, :velocity 
    end 
    end 

    def initialize_helper options 
    @radius = options[:radius] 
    @velocity = options[:velocity] 
    puts "From radius and velocity" 
    end 

    def argument_of_perigee 
    @argument_of_perigee ||= begin 
    end 
    end 
end 

這就是說,這種解決方案只是爲了演示和我建議你遵循@Ollie在他的回答中提到的例子,因爲它是乾淨多了。

0

對我來說,這聽起來就像是簡單的繼承工作:

class FooBar 
    def self.new(options) 
    if [:radius,:velocity].all? { |key| options.has_key?(key) } 
     Foo.new options 
    else 
     Bar.new options 
    end 
    end 

    # common Foo and Barlogic here 
end 

class Foo < FooBar 
end 

class Bar < FooBar 
end 
2

我真的不知道爲什麼你要一個下掩蓋兩個不同的實體的實例,但如果你想基於簡單的決策邏輯參數,這個工程:

# orbit.rb 
module Orbit 
    module_function 

    def from_options(options = {}) 
    klass = if options[:radius] && options[:velocity] 
       FromRadiusAndVelocity 
      else 
       FromOrbitalElements 
      end 

    klass.new(options) 
    end 
end 

# orbit/from_orbital_elements.rb 
module Orbit 
    class FromOrbitalElements 
    attr_reader :argument_of_perigee 

    def initialize(options) 
     @argument_of_perigee = options[:argument_of_perigee] 

     puts 'From orbital elements' 
    end 

    def radius 
     @radius ||= begin 
        # ... 
        end 
    end 
    end 
end 

# orbit/from_radius_and_velocity.rb 
module Orbit 
    class FromRadiusAndVelocity 
    attr_reader :radius, :velocity 

    def initialize(options) 
     @radius = options[:radius] 
     @velocity = options[:velocity] 

     puts 'From radius and velocity' 
    end 

    def argument_of_perigee 
     @argument_of_perigee ||= begin 
           # ... 
           end 
    end 
    end 
end 

# Usage 
orbit = Orbit.from_options(radius: 5, velocity: 6) 
orbit = Orbit.from_options(argument_of_perigee: 5) 

如果你想要做一些相同的接口,啄的,可能要轉軌道到一個類和其他類的子類,但不是從您的代碼清晰。