假设一个人有以下模式:
一个很好的例子是轨道元素计算器,它可能采用半径和速度矢量,计算轨道元素---或者可能采用轨道元素并计算半径和速度矢量。
为什么不以更直接的方式做到这一点?好吧,我想利用实例变量来实现即时计算,例如,
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
是否有更好的模式/这是一个明智的用法?有可能这样做吗?
答案 0 :(得分: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)
如果你想做某种同样的接口,可能想把Orbit变成一个类,并把它子类化在其他类中,但是你的代码并不清楚。
答案 1 :(得分:1)
对我而言,这听起来像是简单继承的工作:
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 :(得分:1)
您可以通过在单例类(有时称为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
如果您打算使用这种方法,那么您需要相应地调整模块:
ClassMethod
个模块,所有包含的方法都是(据说)从一个对象而不是一个类中调用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的例子,因为它更清晰。