我正在设计/构建一个类系统,这些类都派生自一个基类。
目标是使用易于使用的继承宏方法,如下所示:
class Something < A::Base
full_name 'Something that goes bump in the night.'
end
任何代码都应该能够通过类级访问器方法向类询问此信息(或者,可能是规范化/派生信息)。
puts Something.full_name
# => "Some kind of calculated value that may or may not go bump in the night."
鉴于A::Base
包含/ extends / somehow-otherwise-mixes-in两个模块的宏方法都是这样的:
module MacroMethods
private
def full_name(full_name)
# non-trivial, one-time-only set-up code exists here in actual usage
end
end
以及具有类级别访问器方法的模块,其工作方式如下:
module AccessorMethods
public
def full_name
# a non-trivial, runtime-calculated value is returned here in actual usage
end
end
无论我如何将它们混合在一起,我都会在两者之间不断遇到命名冲突(即'错误的参数数量(1为0)(ArgumentError)')。
注意: full_name
是所需内容的最简单示例;其他更复杂的宏/访问器确保宏方法的非灵活约束需要在类中声明,并且需要一次性设置一次。
我的问题是双重的:
A::Base
课程内完成? 已考虑的选项:
调用宏或访问者方法。
(例如,在Something类中:set_up_full_name 'Something that …'
)
缺点是命名令人困惑且非常规。
使访问者方法实例级而不是类级别。
(例如puts a_something.full_name'
)
缺点是宏设置的特征是类固有的特征,而不是每个实例(在某些情况下,只有对类的引用可用,而不是实例)。
创建一个处理宏和访问者功能的方法。
(例如在A ::基类:def self.full_name(*args) …
)
缺点是宏方法不再是私有的,RDoc看起来像sh * t。
改为使用abstact / virtual-ish方法。
(例如,在Something类中:def self.full_name; 'Something that …'; end
)
缺点是这是子类中的更多代码,而不是一个优秀的Ruby范例,更像是Objective-C (或C ++,或Java,...)。
答案 0 :(得分:1)
Slipp,我仔细阅读了你的问题。您无法同时在同一对象上定义两个名为full_name
的不同方法。但是,你可以这样做:
module MacroMethods
private
def full_name(full_name)
# non-trivial, one-time-only set-up code exists here in actual usage
# this method can only be called once in the definition of any given class,
# because after the first call, it will be redefined!
extend AccessorMethods
end
end
module AccessorMethods
public
def full_name
# a non-trivial, runtime-calculated value is returned here in actual usage
end
end
class Base
extend MacroMethods
end
class Something < Base
full_name 'after this call, I will get a new full_name method!'
end
class SomethingElse < Base
full_name 'so will I!'
end
答案 1 :(得分:0)
如果您希望某些类可以使用类宏,那么公共基类不是Ruby解决方案。相反,您创建了一个模块,该模块使用您希望它们具有的功能扩展基类:
module Extensions
def self.included(base_class)
base_class.extend ClassMethods
end
module ClassMethods
attr_accessor :full_name
end
end
class Something
include Extensions
self.full_name = "Something that goes bump in the night"
end
puts Something.full_name # => Something that goes bump in the night
thing = Something.new
puts thing.full_name # Error
这会覆盖名为Extensions
的{{1}}中的钩子方法,该方法将包含该模块的任何类作为参数传递。然后,新方法在基类上调用Module#included
,将Object#extend
中可用的方法作为类方法直接放到该类上。这与定义类方法的工作方式相同,但这是动态运行的。这使您无需在提供宏的类上使用您唯一的基类。请注意,这些方法未在包含该模块的类的实例上定义。
答案 2 :(得分:0)
看起来大多数其他答案都有正确的想法,但缺少#full_name
的getter方法。这个例子可能就是你要找的东西:
class Thing
class << self
attr_writer :full_name
def full_name
"My full name is #{@full_name}"
end
end
end
有了这个,你可以这样做:
> Thing.full_name = 'Thing Class'
=> "Thing Class"
> Thing.full_name
=> "My full name is Thing Class"
答案 3 :(得分:-1)
这似乎是不必要的复杂。为什么不在父类上使用属性?
class Base
class << self
attr_accessor :full_name
end
end
class A < Base; end
class B < Base; end
A.full_name = "The full name of A"
B.full_name = "The full name of B"
puts A.full_name # "The full name of A"
puts B.full_name # "The full name of B"