Zirkuläre Abhängigkeiten in Ruby
Sagen wir, wir haben zwei Klassen, Foo und Foo-Sub, jeder in einer anderen Datei foo.rb und foo_sub.rb bzw.
foo.rb:
require "foo_sub"
class Foo
def foo
FooSub.SOME_CONSTANT
end
end
foo_sub.rb:
require "foo"
class FooSub < Foo
SOME_CONSTANT = 1
end
Dies ist nicht zur Arbeit zu gehen, durch die zirkuläre Abhängigkeit - können wir nicht definieren, entweder in der Klasse ohne das andere. Es gibt verschiedene Lösungen, die ich gesehen habe. Zwei von Ihnen möchte ich vermeiden - nämlich, indem man Sie in die gleiche Datei und entfernen Sie die zirkuläre Abhängigkeit. Also, die einzige andere Lösung, die ich gefunden habe, ist eine vorwärts-Deklaration:
foo.rb:
class Foo
end
require "foo_sub"
class Foo
def foo
FooSub.SOME_CONSTANT
end
end
foo_sub.rb
require "foo"
class FooSub < Foo
SOME_CONSTANT = 1
end
Leider kann ich nicht die gleiche Sache arbeiten, wenn ich habe drei Dateien:
foo.rb:
class Foo
end
require "foo_sub_sub"
class Foo
def foo
FooSubSub.SOME_CONSTANT
end
end
foo_sub.rb:
require "foo"
class FooSub < Foo
end
foo_sub_sub.rb:
require "foo_sub"
class FooSubSub < FooSub
SOME_CONSTANT = 1
end
Wenn ich eine foo_sub.rb, dann FooSub ist eine nicht initialisierte Konstante in foo_sub_sub.rb. Irgendwelche Ideen wie man das umgehen kann, ohne dass Sie in der gleichen Datei noch das entfernen der zirkuläre Abhängigkeit?
- und doch, warum nicht entfernen Sie die zirkuläre Abhängigkeit? Es ist der Hauptgrund, code wird nicht wartbar.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie brauchen, um Zugang zu einer Unterklasse aus einer Oberklasse, dann gibt es eine gute chance, dass Ihr Modell gebrochen ist (d.h. es sollte eine Klasse).
Das heißt, es gibt ein paar offensichtliche Lösungen:
1) erstellen Sie einfach eine Datei, die erfordert, dass die foo-Dateien:
all_foos.rb:
entfernen und das erfordert von foo.rb und foo_sub.rb.
2) entfernen Sie das verlangen, von foo.rb
3) entfernen Sie das verlangen, von foo_sub.rb, und stellen Sie das verlangen in foo.rb nach der definition der Klasse.
Ruby ist nicht C++, ist es nicht beschweren FooSub.SOME_CONSTANT, bis Sie den Aufruf von Foo#foo() 😉
all_foos.rb
in Ihrem ersten Lösung ?Andere anständige Möglichkeit ist die Verwendung der autoload-Funktion in Ruby.
Funktioniert es so:
beschrieben ist, auch hier:
http://talklikeaduck.denhaven2.com/2009/04/06/all-that-you-might-require
Sandi Metz erklärt, eine Lösung für dieses problem und wie zu lösen, es ist wirklich schön in Ihrem Buch " Practical Object-Oriented Design in Ruby (POODR).
Was Sie vorschlägt (und ich bin geneigt zu, einverstanden mit, wie seine arbeitete für mich das beste bisher), ist zu injizieren, die sub-Klasse
FooSub
in der master-KlasseFoo
.Dies geschieht in foo.rb mit:
pflegen, clean code, und halten Sie es leicht veränderbar, Sie würde dann wickeln Sie die
foo_sub
in einer wrapper-Methode, damit Sie Ihre Klasse sieht nun wie folgt aus:(hier die
attr_reader
ist die Einrichtung einer Methode namensfoo_sub
und dann auch immer übergeben wird in den Wert der initialize-hash ist eine Instanz von foo_sub, daher@foo_sub
(Zeile 6), kann eingestellt werden, um den Wert der Methodefoo_sub
).Können Sie jetzt Ihre
FooSub
Klasse mit nicht benötigt, so dass es unabhängig von allem:und können Sie eine Methode hinzufügen, um Ihre
Foo
Klasse, hat Zugriff auf #SOME_CONSTANT:In Wirklichkeit, bist du das einrichten eine Methode gibt die Instanz von foo_sub
@foo_sub
(eingespritzt wird, bei der Initialisierung), mit der Methode #SOME_CONSTANT angehängt, auf es. Ihre Klasse einfach erwartet, was injiziert wird in das initialize-Antworten zu #SOME_CONSTANT. DAMIT Sie funktioniert müsste injizieren SieFooSub
Klasse beim einrichtenFoo
in einer REPL (e.g IRB oder PRY):wenn, jedoch, Sie gespritzt was anderes, würde Sie am Ende mit:
Ich weiß nicht, was die eigentliche Fehlermeldung Lesen würde auf der Linie 10, aber denke, dass entlang dieser Linien. Dieser Fehler wäre aufgetreten, da hätten Sie effektiv versucht, die Methode ausgeführt #SOME_CONSTANT auf den string 'etwas anderes als ein string' oder
'something else as a string'.SOME_CONSTANT
das wäre natürlich nicht arbeiten.