Ruby fornisce un costrutto chiamatoModule
che è un po ‘ come una classe sottile. All’inizio, gli sviluppatori di Ruby potrebbero guardare i moduli come un ” cassetto spazzatura” di codice che viene appena gettato in giro e utilizzato per organizzare i file, ma c’è un metodo per questa follia. La classe RubyClass
eredita daModule
e aggiunge cose come istanziazione, proprietà, ecc. Poiché Module
è letteralmente un antenato di Class
, ciò significa che i moduli possono essere trattati come classi in qualche modo.
La cosa più importante da capire è come funziona la catena degli antenati di Ruby. Come tutti i linguaggi OOP, Ruby supporta l’ereditarietà. Quando si eredita da una classe, la superclasse viene aggiunta alla catena antenato. È possibile visualizzare gli antenati di qualsiasi classe chiamando ancestors
su di esso:
class Foo < BarendFoo.ancestors#=>
Come accennato, è possibile trovare Module
nell’array Class.ancestors
.
Quando si include
un modulo nella classe, il modulo viene aggiunto alla catena antenato della classe – proprio come una classe. Questo rende include
solo una forma di ereditarietà, non c’è nulla di speciale.
module Barendclass Foo include BarendFoo.ancestors#=>
Tutti i metodi inBar
vengono aggiunti aFoo
come metodi di istanza. Poiché Bar è nella catena, puoi anche chiamare super
da esso per chiamare i metodi sopra di esso nella catena, indipendentemente dal fatto che siano definiti su Moduli o Classi.
class Baz def hello p 'world' endendmodule Bar def hello p 'hello' super endendclass Foo < Baz include BarendFoo.new.hello#=> hello world
Anche seBar
è un modulo,super
richiama ancora la catena e quindi viene chiamato ancheBaz#hello
. Vale la pena notare che Bar
viene aggiunto alla catena antenata di fronte a Baz. Quando un modulo è incluso, viene sempre aggiunto direttamente in cima alla sua classe inclusa. Questo può diventare confuso quando si aggiungono più moduli, poiché sono inclusi nell’ordine “inverso”:
class Foo include A include BendFoo.ancestors#=>
QuandoA
è incluso, viene inserito direttamente sopraFoo
. Ma quandoB
è incluso, viene inserito anche direttamente sopraFoo
, quindi finisce per atterrare prima diA
.
include vs extend
include
è abbastanza facile da capire, aggiunge i metodi del modulo come metodi di istanza alla sua classe inclusa. Puoi pensare aextend
facendo la stessa cosa, ma aggiungendo invece i metodi come metodi di classe.
module Bar def hello p 'hello' endendclass Foo extend BarendFoo.hello # no .new!# => 'hello'
antepone
Oltre a includere/estendere, Ruby 2.0+ aggiunge il metodo prepend
prepend
è simile ainclude
, ma inserisce invece il modulo prima della classe including nella catena di ereditarietà.
class Foo include Bar prepend BazendFoo.ancestors#=>
In Ruby puoi riaprire qualsiasi classe e ridefinire i metodi su di essa-non sembra utile sovrascrivere i metodi su una classe usando un modulo anteposto invece di ridefinirli. Doveprepend
è utile quando hai ancora bisogno dell’implementazione originale del metodo, perché i moduli anteposti possono sovrascrivere i metodi ma usano ancorasuper
per chiamare l’implementazione originale.