Ruby bietet ein Konstrukt namens Module
, das einer dünnen Klasse ähnelt. Zunächst könnten Ruby-Entwickler Module wie eine „Junk-Schublade“ aus Code betrachten, die einfach herumgeworfen und zum Organisieren von Dateien verwendet wird, aber es gibt eine Methode für diesen Wahnsinn. Die Ruby-Klasse Class
erbt von Module
und fügt Dinge wie Instanziierung, Eigenschaften usw. hinzu – alles Dinge, die Sie normalerweise für eine Klasse halten würden. Da Module
buchstäblich ein Vorfahr von Class
, bedeutet dies, dass Module in gewisser Weise wie Klassen behandelt werden können.
Am wichtigsten zu verstehen ist, wie die Ruby-Ahnenkette funktioniert. Wie alle OOP-Sprachen unterstützt Ruby Vererbung. Wenn Sie von einer Klasse erben, wird die Oberklasse zu Ihrer Ahnenkette hinzugefügt. Sie können die Vorfahren jeder Klasse anzeigen, indem Sie ancestors
aufrufen:
class Foo < BarendFoo.ancestors#=>
Wie bereits erwähnt, finden Sie Module
im Array Class.ancestors
.
Wenn Sie include
ein Modul in Ihre Klasse einfügen, wird das Modul zur Ahnenkette Ihrer Klasse hinzugefügt – genau wie eine Klasse. Dies macht include
nur zu einer Form der Vererbung, daran ist nichts Besonderes.
module Barendclass Foo include BarendFoo.ancestors#=>
Alle Methoden in Bar
werden als Instanzmethoden zu Foo
hinzugefügt. Da sich Bar in der Kette befindet, können Sie sogar super
aufrufen, um Methoden darüber in der Kette aufzurufen, unabhängig davon, ob sie für Module oder Klassen definiert sind.
class Baz def hello p 'world' endendmodule Bar def hello p 'hello' super endendclass Foo < Baz include BarendFoo.new.hello#=> hello world
Obwohl Bar
ein Modul ist, super
ruft immer noch die Kette auf und so wird auch Baz#hello
aufgerufen. Es ist erwähnenswert, dass Bar
der Ahnenkette vor Baz hinzugefügt wird. Wenn ein Modul enthalten ist, wird es immer direkt über der Including-Klasse hinzugefügt. Dies kann beim Hinzufügen mehrerer Module verwirrend werden, da sie in „umgekehrter“ Reihenfolge enthalten sind:
class Foo include A include BendFoo.ancestors#=>
Wenn A
enthalten ist, wird es direkt über Foo
eingefügt. Wenn jedoch B
enthalten ist, wird es auch direkt über Foo
eingefügt, sodass es vor A
landet.
include vs extend
include
ist leicht genug zu verstehen, es fügt die Methoden des Moduls als Instanzmethoden zu seiner Including-Klasse hinzu. Sie können sich extend
vorstellen, dasselbe zu tun, aber stattdessen die Methoden als Klassenmethoden hinzuzufügen.
module Bar def hello p 'hello' endendclass Foo extend BarendFoo.hello # no .new!# => 'hello'
prepend
Zusätzlich zu include/extend fügt Ruby 2.0+ die prepend
Methode hinzu. prepend
ähnelt include
, fügt jedoch das Modul vor der Including-Klasse in die Vererbungskette ein.
class Foo include Bar prepend BazendFoo.ancestors#=>
In Ruby können Sie jede Klasse erneut öffnen und die Methoden neu definieren – es scheint nicht sinnvoll zu sein, Methoden für eine Klasse mit einem vorangestellten Modul zu überschreiben, anstatt sie neu zu definieren. Wo prepend
nützlich ist, ist, wenn Sie noch die ursprüngliche Implementierung der Methode benötigen, da vorangestellte Module Methoden überschreiben können, aber immer noch super
um die ursprüngliche Implementierung aufzurufen.