Ruby biedt een constructie genaamd Module
wat een soort dunne klasse is. Op het eerste, Ruby devs zou kunnen kijken naar Modules als een “junk lade” van code die gewoon rond gegooid en gebruikt om bestanden te organiseren, maar er is een methode om deze waanzin. De Ruby klasse Class
erft van Module
en voegt dingen toe zoals instantiation, properties, etc – alles wat je normaal zou denken dat een klasse zou hebben. Omdat Module
letterlijk een voorouder is van Class
, betekent dit dat Modules op sommige manieren als klassen kunnen worden behandeld.
het belangrijkste om te begrijpen is hoe de Ruby voorouder keten werkt. Zoals alle OOP talen, Ruby ondersteunt erfenis. Wanneer je erft van een klasse, wordt de superklasse toegevoegd aan je voorouder keten. U kunt de voorouders van elke klasse bekijken door ancestors
op te roepen:
class Foo < BarendFoo.ancestors#=>
zoals vermeld, kunt u Module
vinden in de array Class.ancestors
.
wanneer u include
een module in uw klasse toevoegt, wordt de module toegevoegd aan de voorouderketen van uw klasse – net als een klasse. Dit maakt include
gewoon een vorm van overerving, er is niets speciaals aan.
module Barendclass Foo include BarendFoo.ancestors#=>
alle methoden in Bar
worden toegevoegd aan Foo
als instantiemethoden. Omdat Bar zich in de keten bevindt, kunt u zelfs super
van daaruit aanroepen om methoden daarboven in de keten aan te roepen, of ze nu gedefinieerd zijn op Modules of klassen.
class Baz def hello p 'world' endendmodule Bar def hello p 'hello' super endendclass Foo < Baz include BarendFoo.new.hello#=> hello world
hoewel Bar
een module is, roept super
nog steeds de keten aan en wordt Baz#hello
ook genoemd. Het is vermeldenswaard dat Bar
wordt toegevoegd aan de voorouderketen voor Baz. Wanneer een module is opgenomen, wordt het altijd direct toegevoegd bovenop het Inclusief klasse. Dit kan verwarrend worden bij het toevoegen van meerdere modules, omdat ze zijn opgenomen in “omgekeerde” volgorde:
class Foo include A include BendFoo.ancestors#=>
wanneer A
is opgenomen, wordt het direct boven Foo
ingevoegd. Maar als B
is opgenomen, wordt het ook direct boven Foo
ingevoegd, zodat het eindigt vóór A
.
include vs extend
include
is gemakkelijk genoeg om te begrijpen, het voegt de methoden van de module als instantie methoden aan het Inclusief klasse. U kunt denken aan extend
die hetzelfde doet, maar in plaats daarvan de methoden toevoegen als klassenmethoden.
module Bar def hello p 'hello' endendclass Foo extend BarendFoo.hello # no .new!# => 'hello'
prepend
naast include/extend voegt Ruby 2.0+ de prepend
methode toe. prepend
is vergelijkbaar met include
, maar voegt in plaats daarvan de module in voor de inclusief-klasse in de overerving-keten.
class Foo include Bar prepend BazendFoo.ancestors#=>
in Ruby kunt u elke klasse opnieuw openen en de methoden daarop opnieuw definiëren-het lijkt niet nuttig om methoden op een klasse te overschrijven met behulp van een vooraf ingestelde module in tegenstelling tot ze opnieuw te definiëren. Waar prepend
van pas komt is wanneer u nog steeds de oorspronkelijke implementatie van de methode nodig hebt, omdat vooraf ingestelde modules methoden kunnen overschrijven, maar nog steeds super
gebruiken om de oorspronkelijke implementatie aan te roepen.