2017-04-07 124 views
1

我正在创建一个类(例如,Bar),以便通过另一个类的方法(比如说,Foo#bar)返回一个对象,几乎是MatchData对象由Regexp#match返回。Ruby - 如何在没有.new的情况下创建MatchData对象?

但类MatchData没有.new

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

问题:

  1. 内部,如何MatchData对象,而无需创建.new
  2. 我该如何实现它(模仿MatchData或不)?

回答

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

+0

很多,您可以通过使类私人做的核心类由于各种原因违反了规则。有时候,他们为自己的特殊而走出困境令人非常痛心。 – tadman

+0

@tadman违反规则? – Stefan

+0

你知道,无法初始化的对象,它们基本上是其他进程的神奇副产品。和Fixnum一样,这不是你可以调用'Fixnum.new'的地方,因为在内部,整数实际上只是对象。 – tadman

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 
+0

我真的很讨厌'整数'。和“浮动”,这是最糟糕的。更不用说'方法'和'编码'。哦,还有那双邪恶的双胞胎,'TrueClass'和'FalseClass' :-) – Stefan

+0

@Stefan,对吧?它是可怕的。给我们'#新'或者让我们死!严肃地说,那里只是内部的噱头。对于用户创建的类,没有人应该这样做。 – ndn

+0

如果核心类没问题,应该允许用户定义的类,不是吗?一个匿名或无价值的'Integer'实例会有点奇怪。 – Stefan