我正在为另一个类的方法(比如Bar
)返回的对象创建一个类(比如Foo#bar
),几乎是MatchData
个对象由Regexp#match
返回。
但是课程MatchData
没有.new
!
我知道我不需要模仿MatchData
实施,但我想了解它并知道如何在我发现它有趣时这样做。假设我不希望客户创建Bar
个对象,除非通过调用Foo#bar
。
问题:
MatchData
如何在没有.new
的情况下创建对象?MatchData
或不模仿?)答案 0 :(得分:3)
MatchData.new
方法为explicitly undefined:
rb_cMatch = rb_define_class("MatchData", rb_cObject);
rb_define_alloc_func(rb_cMatch, match_alloc);
rb_undef_method(CLASS_OF(rb_cMatch), "new"); // <- here
您可以通过undef_method
在纯Ruby中执行相同操作:
class Bar
class << self
undef_method :new
end
def initialize
@bar = '123' # <- for demonstration purposes
end
end
尝试致电Bar.new
现在会导致错误:
Bar.new #=> undefined method `new' for Bar:Class (NoMethodError)
要创建不使用new
方法的新实例,您可以手动调用allocate
(也可能initialize
):
bar = Bar.allocate #=> #<Bar:0x007f9eba047cd8>
Bar.send(:initialize) #=> "123"
bar #=> #<Bar:0x007fd8e0847658 @bar="123">
(send
是必需的,因为initialize
是私有的)
答案 1 :(得分:1)
首先我要说明你不应该这样做。即使它不是公共界面,也可以通过限制用户来做他们想做的事情。一种更惯用的方法是让它更明确,它不是公共接口的一部分。您可以通过将类设为私有来实现:
class RegexMockery
class MatchDataMockery
def initialize(whatever)
puts "I'm being created #{whatever}"
end
def [](_)
'42'
end
end
private_constant :MatchDataMockery
def match(string)
MatchDataMockery.new(string)
end
end
match_result = RegexMockery.new.match('foo')
# I'm being created foo
# => #<RegexMockery::MatchDataMockery:0x007fe990de2ed0>
match_result[0] # => '42'
RegexMockery::MatchDataMockery # !> NameError: private constant RegexMockery::MatchDataMockery referenced
但是如果你坚持让人讨厌你,请保存方法,取消它并在你想创建实例时调用它:
class Foo
def initialize(whatever)
puts "Now you see me #{whatever}"
end
def brag
puts "I can create Foos and you can't!!!1!!"
end
end
class Bar
foos_new = Foo.method(:new)
Foo.singleton_class.send :undef_method, :new
define_method(:sorcery) do
foos_new.call('bar').brag
end
end
Bar.new.sorcery
# Now you see me bar
# I can create Foos and you can't!!!1!!
Foo.new # !> NoMethodError: undefined method `new' for Foo:Class