Ruby - 如何在没有 .new 的情况下创建 MatchData 对象



我正在为一个对象创建一个类(比如,Bar(,由另一个类(比如Foo#bar(的方法返回,几乎MatchData对象是由Regexp#match返回的。

但是班级MatchData没有.new

我知道我不需要模仿MatchData实现,但我想理解它,并在发现它有趣时知道如何去做。假设我不希望客户端创建Bar对象,除非通过调用 Foo#bar .

问题:

  1. 在内部,如何在没有.new的情况下创建MatchData对象?
  2. 我该如何实现它(模仿或不模仿MatchData(?

MatchData.new 方法显式未定义:

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是私有的(

让我从你不应该开始。不遗余力地约束用户做他们想做的事情,即使它不是公共接口,这也是不雅的。一种更惯用的方法是更明确地表明它不是公共接口的一部分。您可以通过将类设为私有来做到这一点:

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

相关内容

最新更新