Ruby, di per sé, non permette l'ereditarietà multipla: ogni classe non può avere più di una superclasse. È possibile però aggirare questo "problema", che a detta di molti problema non è, attraverso l'utilizzo dei moduli e il meccanismo del mixin.
Oltre al classico utilizzo per la creazione di namespace, i moduli possono essere utilizzati per implementare una sorta di ereditarietà multipla. Vediamo innanzitutto cosa si intende per modulo e come si definisce.
I moduli sono dei contenitori di metodi, costanti e classi e, alla stessa maniera delle classi, devono avere un nome che inizia per lettera maiuscola. Sono definiti dalla parola chiave module
; un esempio è il seguente:
module Net class HTTP def HTTP.get_print(uri_or_host, path = nil, port = nil) ... end end end
Per poter accedere alle classi del modulo occorre importalo con l'istruzione require
e poi fare riferimento alle classi con il nome esteso. Ad esempio se si vuole utilizzare il metodo get_print
della classe HTTP del modulo Net occorre scrivere:
require 'net/http' Net::HTTP.get_print 'www.google.com', '/index.html'
La creazione di un namespace permette sia di evitare problemi con i nomi, sia di rendere il codice più coerente e pulito. Oltre a ciò, come anticipato prima, è possibile utilizzare i moduli in maniera meno ortodossa ma molto efficace. Per ovviare alla mancanza dell'ereditarietà multipla basta racchiudere in un modulo i metodi che vogliamo "far ereditare" alla nostra classe e quindi includerlo con l'istruzione include
. Di conseguenza possiamo utilizzare tutti i metodi del modulo dall'interno della nostra classe come dei normali metodi che vengono mescolati, mixed in appunto, a quelli definiti dalla classe e a quelli ereditati dalla superclasse.
Un esempio ci chiarirà tutto. Creiamo un semplice modulo con un solo metodo che non fa altro che stampare una frase a video:
module Inutile def hello "Saluti da #{self.class.name}" end end class Tokyo include Inutile end
Istanziando una nuova classe possiamo chiamare il metodo hello ereditato dal modulo ottenendo:
> tk = Tokyo.new > tk.hello => "Saluti da Tokyo"
In questo modo è possibile aggiungere alle classi una gran quantità di metodi senza nessuna fatica. Ad esempio possiamo dotare le nostre classi dei metodi di confronto includendo Comparable
, o anche aggiungere dei metodi di ricerca e ordinamento includendo Enumerable
e così via.