2017-04-07 3 views
1

Ich erstelle eine Klasse (z. B. Bar) für ein Objekt von einer Methode einer anderen Klasse (z. B. Foo#bar) zurückgegeben werden, so ziemlich Objekt wird von Regexp#match zurückgegeben.Ruby - Wie wird das MatchData-Objekt ohne .new erstellt?

Aber die Klasse MatchData hat nicht .new!

Ich weiß, ich muss MatchData Implementierung nicht nachahmen, aber ich würde es gerne verstehen und wissen, wie es geht, wenn ich es interessant finde. Angenommen, ich möchte nicht, dass Clients Bar Objekte erstellen, es sei denn, sie rufen Foo#bar auf.

Fragen:

  1. Intern wie wird MatchData Objekt ohne .new erstellt?
  2. Wie kann ich es (imitieren MatchData oder nicht) implementieren?

Antwort

3

Die MatchData.new Methode wird 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 

Sie können über undef_method das gleiche in reinem Rubin tun:

class Bar 
    class << self 
    undef_method :new 
    end 

    def initialize 
    @bar = '123' # <- for demonstration purposes 
    end 
end 

Versuch Bar.new nennen wird nun zu einem Fehler:

Bar.new #=> undefined method `new' for Bar:Class (NoMethodError) 

Um eine neue Instanz ohne new Methode zu erstellen, geben Sie allocate manuell aufrufen können (und vielleicht initialize auch):

bar = Bar.allocate  #=> #<Bar:0x007f9eba047cd8> 
Bar.send(:initialize) #=> "123" 
bar     #=> #<Bar:0x007fd8e0847658 @bar="123"> 

(send benötigt wird, weil initialize privat)

+0

Viele von Kernklassen brechen die Regeln aus verschiedenen Gründen. Manchmal ist es ziemlich peinlich, dass sie sich besonders bemüht haben, etwas Besonderes zu sein. – tadman

+0

@tadman die Regeln brechen? – Stefan

+0

Wissen Sie, Objekte, die nicht initialisiert werden können, sind im Grunde magische Nebenprodukte anderer Prozesse. Wie Fixnum ist es nicht etwas, das man "Fixnum.new" nennen kann, weil Integer intern eigentlich nur Objekte sind. – tadman

1

mich damit anfangen Lassen du solltest nicht. Es ist sehr schwierig, die Benutzer dazu zu zwingen, das zu tun, was sie tun möchten, auch wenn es keine öffentliche Schnittstelle ist. Ein mehr idiomatischer Ansatz würde es deutlicher machen, dass es nicht Teil der öffentlichen Schnittstelle ist. Sie können das tun, um die Klasse, indem sie private:

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 

Aber wenn man auf die Menschen darauf bestehen, hassen Sie, speichern Sie die Methode, undef es und es nennen, wenn Sie Instanzen erstellen möchten:

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

Ich hasse 'Integer'. Und 'Float', es ist das Schlimmste. Ganz zu schweigen von 'Method' und' Encoding'. Oh, und die bösen Zwillinge 'TrueClass' und' FalseClass' :-) – Stefan

+0

@Stefan, richtig, richtig? Es ist schrecklich. Gib uns '# new' oder gib uns den Tod!In aller Ernsthaftigkeit gibt es dort nur interne Gimmicks. Für von Benutzern erstellte Klassen sollte dies niemand tun. – ndn

+0

Wenn es für Core-Klassen in Ordnung ist, sollte es auch für benutzerdefinierte Klassen erlaubt sein, oder? Eine anonyme oder wertfreie Integer-Instanz wäre wenig seltsam. – Stefan

Verwandte Themen