Weird Ruby-Klasseninitialisierungslogik?

8

Einige Open-Source-Code, den ich in meine Anwendung integriere, haben einige Klassen, die Code zu diesem Effekt enthalten:

%Vor%

Soviel ich verstehe, machen sowohl self.new als auch initialize dasselbe - das letztere "während der Konstruktion" und das vorherige "nach der Konstruktion", und es sieht für mich wie ein schreckliches Muster aus - Warum teilen Sie die Objektinitialisierung in zwei Teile auf, wobei einer offensichtlich "The Wrong Think (tm)" ist?

    
Guss 25.04.2017, 09:16
quelle

3 Antworten

4

Idealerweise würde ich gerne sehen, was sich im Block super().tap { |o| befindet, denn obwohl das wie eine schlechte Praxis aussieht, ist vielleicht nur eine Interaktion erforderlich, bevor oder nachdem initialize aufgerufen wird.

Ohne Kontext ist es möglich, dass Sie nur etwas betrachten, das funktioniert, aber in Ruby nicht als gute Praxis angesehen wird.

Der Ansatz der separaten Methoden self.new und initialize ermöglicht es dem Framework-Designer jedoch, einen Teilklassen-fähigen Teil des Frameworks zu implementieren und dennoch sicherzustellen, dass die für das Framework erforderlichen Einstellungen ohne eine leicht umständliche Dokumentation, die ein bestimmtes erfordert, durchgeführt werden Verwendung von super() . Es wäre eine leichter zu dokumentierende und sauberere API, wenn der Endbenutzer die Funktionalität erhält, die er nur mit der Unterklasse class MyClass < FrameworkClass erwartet und ohne zusätzliche Hinweise wie:

  

Wenn Sie die Unterklasse initialize implementieren, denken Sie daran, super an den Anfang zu setzen, sonst funktioniert die Magie nicht

. . . persönlich würde ich dieses Design als fragwürdig empfinden, aber ich denke, dass es zumindest eine klare Motivation geben würde.

Es könnte tiefere Ruby-Gründe geben, Code in einem benutzerdefinierten self.new -Block laufen zu lassen - zum Beispiel kann es dem Konstruktor erlauben, das spezifische Objekt zu wechseln oder zu ändern (sogar ein Objekt einer anderen Klasse zurückzugeben), bevor es es zurückgibt. Allerdings habe ich solche Dinge selten in der Praxis gesehen, es gibt fast immer einen anderen Weg, die Ziele eines solchen Codes zu erreichen, ohne new anzupassen.

Beispiele für benutzerdefinierte / verschiedene Class.new -Methoden, die in den Kommentaren angesprochen wurden:

  • Struct.new , das optional ein Klassenname und Rückgabeobjekte dieser dynamisch erstellten Klasse.

  • In-table-Vererbung für ActiveRecord , die ermöglicht dem Endbenutzer, ein Objekt unbekannter Klasse aus einer Tabelle zu laden und das richtige Objekt zu erhalten.

Letzteres könnte möglicherweise mit einem anderen ORM-Design für die Vererbung vermieden werden (obwohl alle diese Schemata Vor- / Nachteile haben).

Der erste (Structs) ist der Kern der Sprache, also muss er jetzt so funktionieren (obwohl die Designer einen anderen Methodennamen gewählt haben könnten).

    
Neil Slater 25.04.2017 10:36
quelle
3

Es ist unmöglich zu sagen, warum dieser Code da ist, ohne den Rest des Codes zu sehen.

Es gibt jedoch etwas in Ihrer Frage, das ich ansprechen möchte:

  

Soviel ich verstehe, machen sowohl self.new als auch initialize dasselbe - das letztere "während des Baus" und das vorherige "nach dem Bau"

Sie machen nicht das Gleiche.

Die Objektkonstruktion in Ruby erfolgt in zwei Schritten: Class#allocate weist a neues leeres Objekt aus dem Objektraum und setzt seinen internen Klassenzeiger auf self . Dann initialisieren Sie das leere Objekt mit einigen Standardwerten. Üblicherweise wird diese Initialisierung von einer Methode namens initialize durchgeführt, aber das ist nur eine Konvention; Die Methode kann beliebig bezeichnet werden.

Es gibt eine zusätzliche Hilfsmethode namens Class#new , die nichts anderes ausführt als das Ausführen die zwei Schritte in Folge, für die Bequemlichkeit des Programmierers:

%Vor%     
Jörg W Mittag 25.04.2017 12:50
quelle
2
___ qstnhdr ___ Weird Ruby-Klasseninitialisierungslogik? ___ tag123ruby ___ Ruby ist eine multi-plattform Open-Source, dynamische objektorientierte interpretierte Sprache, erstellt von Yukihiro Matsumoto (Matz) im Jahr 1995. Die [Ruby] -Tag ist für Fragen im Zusammenhang mit der Ruby-Sprache, einschließlich seiner Syntax und seiner Bibliotheken . Fragen speziell zum Ruby-on-Rails-Framework sollten mit [ruby-on-rails], nicht [ruby] getaggt werden. Verwenden Sie dies nicht, um Ruby in der japanischen Sprache zu markieren. ___ tag123constructor ___ Eine spezielle Art von Subroutine, die bei der Erstellung eines Objekts aufgerufen wird. ___ qstntxt ___

Einige Open-Source-Code, den ich in meine Anwendung integriere, haben einige Klassen, die Code zu diesem Effekt enthalten:

%Vor%

Soviel ich verstehe, machen sowohl new als auch initialize dasselbe - das letztere "während der Konstruktion" und das vorherige "nach der Konstruktion", und es sieht für mich wie ein schreckliches Muster aus - Warum teilen Sie die Objektinitialisierung in zwei Teile auf, wobei einer offensichtlich "The Wrong Think (tm)" ist?

    
___ answer43611161 ___

Es ist unmöglich zu sagen, warum dieser Code da ist, ohne den Rest des Codes zu sehen.

Es gibt jedoch etwas in Ihrer Frage, das ich ansprechen möchte:

  

Soviel ich verstehe, machen sowohl initialize als auch new dasselbe - das letztere "während des Baus" und das vorherige "nach dem Bau"

Sie machen nicht das Gleiche.

Die Objektkonstruktion in Ruby erfolgt in zwei Schritten: %code% weist a neues leeres Objekt aus dem Objektraum und setzt seinen internen Klassenzeiger auf %code% . Dann initialisieren Sie das leere Objekt mit einigen Standardwerten. Üblicherweise wird diese Initialisierung von einer Methode namens %code% durchgeführt, aber das ist nur eine Konvention; Die Methode kann beliebig bezeichnet werden.

Es gibt eine zusätzliche Hilfsmethode namens %code% , die nichts anderes ausführt als das Ausführen die zwei Schritte in Folge, für die Bequemlichkeit des Programmierers:

%Vor%     
___ answer43608327 ___

Idealerweise würde ich gerne sehen, was sich im Block %code% befindet, denn obwohl das wie eine schlechte Praxis aussieht, ist vielleicht nur eine Interaktion erforderlich, bevor oder nachdem %code% aufgerufen wird.

Ohne Kontext ist es möglich, dass Sie nur etwas betrachten, das funktioniert, aber in Ruby nicht als gute Praxis angesehen wird.

Der Ansatz der separaten Methoden %code% und %code% ermöglicht es dem Framework-Designer jedoch, einen Teilklassen-fähigen Teil des Frameworks zu implementieren und dennoch sicherzustellen, dass die für das Framework erforderlichen Einstellungen ohne eine leicht umständliche Dokumentation, die ein bestimmtes erfordert, durchgeführt werden Verwendung von %code% . Es wäre eine leichter zu dokumentierende und sauberere API, wenn der Endbenutzer die Funktionalität erhält, die er nur mit der Unterklasse %code% erwartet und ohne zusätzliche Hinweise wie:

  

Wenn Sie die Unterklasse %code% implementieren, denken Sie daran, %code% an den Anfang zu setzen, sonst funktioniert die Magie nicht

. . . persönlich würde ich dieses Design als fragwürdig empfinden, aber ich denke, dass es zumindest eine klare Motivation geben würde.

Es könnte tiefere Ruby-Gründe geben, Code in einem benutzerdefinierten %code% -Block laufen zu lassen - zum Beispiel kann es dem Konstruktor erlauben, das spezifische Objekt zu wechseln oder zu ändern (sogar ein Objekt einer anderen Klasse zurückzugeben), bevor es es zurückgibt. Allerdings habe ich solche Dinge selten in der Praxis gesehen, es gibt fast immer einen anderen Weg, die Ziele eines solchen Codes zu erreichen, ohne %code% anzupassen.

Beispiele für benutzerdefinierte / verschiedene %code% -Methoden, die in den Kommentaren angesprochen wurden:

  • Struct.new , das optional ein Klassenname und Rückgabeobjekte dieser dynamisch erstellten Klasse.

  • In-table-Vererbung für ActiveRecord , die ermöglicht dem Endbenutzer, ein Objekt unbekannter Klasse aus einer Tabelle zu laden und das richtige Objekt zu erhalten.

Letzteres könnte möglicherweise mit einem anderen ORM-Design für die Vererbung vermieden werden (obwohl alle diese Schemata Vor- / Nachteile haben).

Der erste (Structs) ist der Kern der Sprache, also muss er jetzt so funktionieren (obwohl die Designer einen anderen Methodennamen gewählt haben könnten).

    
___ antwort43607170 ___

Der -Konstruktor %code% muss eine Klassenmethode sein, da Sie dort beginnen, wo keine Instanz ist; Sie können diese Methode nicht für eine bestimmte Instanz aufrufen. Auf der anderen Seite ist eine Initialisierung Routine %code% besser als Instanzmethode definiert, weil Sie etwas speziell mit einer bestimmten Instanz machen wollen. Daher ist Ruby so konzipiert, dass die Instanzmethode %code% direkt nach ihrer Erstellung von der Klassenmethode %code% intern aufgerufen wird.

    
___
sawa 25.04.2017 09:47
quelle

Tags und Links