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

时间:2017-04-07 08:09:46

标签: ruby class instantiation

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

但是课程MatchData没有.new

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

问题:

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

2 个答案:

答案 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