Ruby 是否可以在初始化期间包含模块?
假设有以下模式:Ruby 是否可以在初始化期间包含模块?,ruby,metaprogramming,Ruby,Metaprogramming,假设有以下模式: Foobar类,它使用Foo或Bar中的逻辑,但不能同时使用两者,具体取决于构造函数给出的参数 Foo模块,包含一些逻辑 Bar模块,包含一些备用逻辑 一个很好的例子是轨道元素计算器,它可以计算半径和速度向量,然后计算轨道元素——或者,可以计算轨道元素,然后计算半径和速度向量 为什么不以更直接的方式来做呢?我想利用实例变量进行实时计算,例如 def derived_value @derived_value ||= begin # compute only whe
- Foobar类,它使用Foo或Bar中的逻辑,但不能同时使用两者,具体取决于构造函数给出的参数
- Foo模块,包含一些逻辑
- Bar模块,包含一些备用逻辑
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
类轨道
def初始化选项={}
如果[:半径,:速度]。全部?{| key | options.has|key?(key)}
#提供从半径、速度导出轨道元素的实例方法
self.include轨道::from半径和速度
其他的
#提供从轨道元素导出半径和速度的实例方法
self.include轨道::FromOrbitalElements
结束
初始化辅助程序选项
结束
结束
模块轨道::来自轨道元素
模块类方法
类对我来说,这听起来像是一个简单继承的工作:
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
class FooBar
def自我更新(选项)
如果[:半径,:速度]。全部?{| key | options.has|key?(key)}
Foo.新选项
其他的
新选项
结束
结束
#这里有常见的Foo和Barlogic
结束
类Foo
对我来说,这听起来像是一项简单继承的工作:
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
class FooBar
def自我更新(选项)
如果[:半径,:速度]。全部?{| key | options.has|key?(key)}
Foo.新选项
其他的
新选项
结束
结束
#这里有常见的Foo和Barlogic
结束
类Foo
我不太清楚为什么要在一个实体下屏蔽两个不同实体的实例,但如果需要基于参数的简单决策逻辑,这是可行的:
# 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转换为一个类并在其他类中对其进行子类化,但这在您的代码中并不清楚。我不太清楚为什么要将两个不同实体的实例屏蔽在一个实体下,但如果您想要基于参数的简单决策逻辑,这是可行的:
# 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变成一个类,然后在其他类中对它进行子类化,但这在你的代码中并不清楚。你可以通过在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
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
如果要使用此方法,则需要相应地调整模块:
不需要ClassMethod
模块,所有包含的方法(假定)都是从对象调用的,而不是从类调用的
从方法名称中删除self.
(当然,包含的self除外)
代码的其余部分可以如下所示
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
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示例,因为它更干净。您可以通过在singleton类(有时称为本征类)中包含选择的模块,而不是对象本身。初始值设定项可以是这样的
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
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
如果要使用此方法,则需要相应地调整模块:
不需要ClassMethod
模块,所有包含的方法(假定)都是从对象调用的,而不是从类调用的
从方法名称中删除self.
(当然,包含的self除外)
代码的其余部分可以如下所示
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
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示例,因为它更干净。不清楚您要做什么。您不能将模块包含到对象中,只有模块的实例定义了此方法。但是,您可以扩展对象,但不能使用您编写的模块。请更新你的问题,清楚地描述你想要达到的目标。现在更清楚了吗?我不太确定我没有充分解释什么,但我很乐意尝试清理那些令人困惑的部分——如果你能指出它们给我看的话。你想做什么还不清楚。你不能将模块包含到一个对象中,只有模块的实例定义了这个方法。但是,您可以扩展对象,但不能使用您编写的模块。请更新你的问题,清楚地描述你想要达到的目标。现在更清楚了吗?我不太确定我没有充分解释什么,但我很乐意尝试清理那些令人困惑的部分——如果你能向我指出的话。