2014-10-07 50 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) 

如果你想要做一些相同的接口,啄的,可能要转轨道到一个类和其他类的子类,但不是从您的代码清晰。