Ich weiß, dass es in Ruby kein Konzept für abstrakte Klasse gibt. Aber wenn es überhaupt implementiert werden muss, wie geht man vor? Ich habe so etwas wie versucht ...
class A
def self.new
raise 'Doh! You are trying to write Java in Ruby!'
end
end
class B < A
...
...
end
Aber wenn ich versuche, B zu instanziieren, wird es intern aufrufen, A.new
was die Ausnahme auslösen wird.
Module können auch nicht instanziiert, aber auch nicht vererbt werden. Die neue Methode privat zu machen, funktioniert ebenfalls nicht. Irgendwelche Hinweise?
ruby
abstract-class
Chirantan
quelle
quelle
raise "Doh! You are trying to write Java in Ruby"
.Antworten:
Ich mag es nicht, abstrakte Klassen in Ruby zu verwenden (es gibt fast immer einen besseren Weg). Wenn Sie wirklich der Meinung sind, dass dies die beste Technik für die Situation ist, können Sie das folgende Snippet verwenden, um aussagekräftiger zu sein, welche Methoden abstrakt sind:
Grundsätzlich rufen Sie nur
abstract_methods
mit der Liste der abstrakten Methoden auf, und wenn sie von einer Instanz der abstrakten Klasse aufgerufen werden, wird eineNotImplementedError
Ausnahme ausgelöst.quelle
NotImplementedError
Wesentlichen "plattformabhängig, bei Ihnen nicht verfügbar" bedeutet. Siehe Dokumente .Um hier spät dran zu sein, gibt es meines Erachtens keinen Grund, jemanden davon abzuhalten, die abstrakte Klasse zu instanziieren, insbesondere weil er ihr im laufenden Betrieb Methoden hinzufügen kann .
Entensprachsprachen wie Ruby verwenden das Vorhandensein / Fehlen oder Verhalten von Methoden zur Laufzeit, um zu bestimmen, ob sie aufgerufen werden sollen oder nicht. Daher ist Ihre Frage, wie sie für eine abstrakte Methode gilt , sinnvoll
und das sollte ungefähr das Ende der Geschichte sein. Der einzige Grund, abstrakte Klassen in Java zu verwenden, besteht darin, darauf zu bestehen, dass bestimmte Methoden "ausgefüllt" werden, während andere ihr Verhalten in der abstrakten Klasse haben. In einer Ententypisierungssprache liegt der Fokus auf Methoden, nicht auf Klassen / Typen, daher sollten Sie Ihre Sorgen auf diese Ebene verschieben.
In Ihrer Frage versuchen Sie im Grunde, das
abstract
Schlüsselwort aus Java neu zu erstellen. Dies ist ein Code-Geruch für Java in Ruby.quelle
Versuche dies:
quelle
#initialize
von B zu verwenden, können Sie einfach alles in A # -Initialisierung erhöhenif self.class == A
.quelle
Für alle in der Rails-Welt erfolgt die Implementierung eines ActiveRecord-Modells als abstrakte Klasse mit dieser Deklaration in der Modelldatei:
quelle
Meine 2 ¢: Ich entscheide mich für ein einfaches, leichtes DSL-Mixin:
In diesem Fall wäre es natürlich trivial, einen weiteren Fehler zum Initialisieren der Basisklasse hinzuzufügen.
quelle
err = NotImplementedError.new(message); err.set_backtrace caller()
YMMVIn den letzten 6 1/2 Jahren der Programmierung von Ruby habe ich kein einziges Mal eine abstrakte Klasse benötigt .
Wenn Sie denken, Sie brauchen eine abstrakte Klasse, denken Sie zu viel in einer Sprache, die sie bereitstellt / erfordert, nicht in Ruby als solcher.
Wie andere vorgeschlagen haben, ist ein Mixin besser für Dinge geeignet, die Schnittstellen sein sollen (wie Java sie definiert), und ein Überdenken Ihres Designs ist besser für Dinge geeignet, die abstrakte Klassen aus anderen Sprachen wie C ++ "benötigen".
Update 2019: Ich habe in 16½ Jahren keine abstrakten Klassen in Ruby benötigt. Alles, was alle Leute, die meine Antwort kommentieren, sagen, wird dadurch angegangen, dass sie Ruby lernen und die entsprechenden Tools wie Module verwenden (die Ihnen sogar allgemeine Implementierungen bieten). Es gibt Leute in Teams, die ich geleitet habe und die Klassen erstellt haben, deren Basisimplementierung fehlschlägt (wie eine abstrakte Klasse), aber diese sind meistens eine Verschwendung von Codierung, da
NoMethodError
sie genau das gleiche Ergebnis wieAbstractClassError
in der Produktion liefern würden .quelle
Sie können 3 Rubygems ausprobieren:
Interface
Abstract
Simple Abstract
quelle
Persönlich löse ich NotImplementedError in Methoden abstrakter Klassen aus. Aber vielleicht möchten Sie es aus den von Ihnen genannten Gründen aus der "neuen" Methode herauslassen.
quelle
initialize
Methoden der Eltern nicht automatisch aufgerufen, es sei denn, sie werden explizit mit super aufgerufen.Wenn Sie mit einer nicht instabilisierbaren Klasse arbeiten möchten, überprüfen Sie in Ihrer A.new-Methode, ob self == A ist, bevor Sie den Fehler auslösen.
Aber wirklich, ein Modul scheint eher das zu sein, was Sie hier wollen - zum Beispiel ist Enumerable die Art von Dingen, die eine abstrakte Klasse in anderen Sprachen sein könnten. Sie können sie technisch nicht unterordnen, aber das Anrufen
include SomeModule
erreicht ungefähr das gleiche Ziel. Gibt es einen Grund, warum dies bei Ihnen nicht funktioniert?quelle
Welchen Zweck möchten Sie mit einer abstrakten Klasse erfüllen? Es gibt wahrscheinlich einen besseren Weg, dies in Rubin zu tun, aber Sie haben keine Details angegeben.
Mein Zeiger ist dies; Verwenden Sie ein Mixin ohne Vererbung.
quelle
Eine andere Antwort:
Dies hängt von der normalen #method_missing ab, um nicht implementierte Methoden zu melden, verhindert jedoch, dass abstrakte Klassen implementiert werden (selbst wenn sie eine Initialisierungsmethode haben).
Wie die anderen Poster bereits sagten, sollten Sie wahrscheinlich eher ein Mixin als eine abstrakte Klasse verwenden.
quelle
Ich habe es so gemacht, also definiert es neu in der Kinderklasse neu, um eine neue in der nicht abstrakten Klasse zu finden. Ich sehe immer noch kein Praktikum darin, abstrakte Klassen in Ruby zu verwenden.
quelle
Es gibt auch dieses kleine
abstract_type
Juwel, mit dem abstrakte Klassen und Module unauffällig deklariert werden können.Beispiel (aus der Datei README.md ):
quelle
An Ihrem Ansatz ist nichts auszusetzen. Das Auslösen eines Fehlers bei der Initialisierung scheint in Ordnung zu sein, solange natürlich alle Ihre Unterklassen die Initialisierung überschreiben. Aber du willst dich nicht so neu definieren. Folgendes würde ich tun.
Ein anderer Ansatz wäre, all diese Funktionen in ein Modul zu integrieren, das, wie Sie bereits erwähnt haben, niemals initiiert werden kann. Nehmen Sie dann das Modul in Ihre Klassen auf, anstatt es von einer anderen Klasse zu erben. Dies würde jedoch Dinge wie super brechen.
Es kommt also darauf an, wie Sie es strukturieren möchten. Obwohl Module wie eine sauberere Lösung zur Lösung des Problems "Wie schreibe ich einige Dinge, die für andere Klassen verwendet werden sollen" erscheinen.
quelle
2-zeiliges Juwel: https://rubygems.org/gems/abstract
quelle
Obwohl sich das nicht nach Ruby anfühlt, können Sie dies tun:
Die Ergebnisse:
quelle