La Gioia del Delegare
Posted by Chiaroscuro, Sat Oct 21 23:30:06 UTC 2006
Se anche voi siete come me, il vecchio libro PikeAxe dei pragmatic programmers che trovate online, rimane la vostra principale reference guide quando volete sapere quali metodi ha una classe o quali classi sono disponibili.
Utilissimo libro, però non copre tutto.. e questo l’ho scoperto frugando tra le lib nella directory di installazione di Ruby.
Tra i vari rubini che ho trovato in quelle directory, forwardable è uno di quelli che più mi ha colpito per la sua utilità.
Se anche voi siete come me, il vecchio libro PikeAxe dei pragmatic programmers che trovate online, rimane la vostra principale reference guide quando volete sapere quali metodi ha una classe o quali classi sono disponibili.
Utilissimo libro, però non copre tutto.. e questo l’ho scoperto frugando tra le lib nella directory di installazione di Ruby.
Tra i vari rubini che ho trovato in quelle directory, forwardable è uno di quelli che più mi ha colpito per la sua utilità. Mettete che avete bisogno di rappresentare un’entità che agisce come contenitore per altre entità. Perchè no, abbozziamo una classe Cassetto!
class Cassetto
end
cassetto_dell_armadio = Cassetto.new
cassetto_dell_armadio << "calzini"
cassetto_dell_armadio << "maglietta"
cassetto_dell_armadio << "maglietta"
puts cassetto_dell_armadio
Per ora abbiamo solamente descritto il comportamento della classe, senza specificarne la struttura interna. Notiamo immediatamente che il nostro Cassetto condivide molte caratteristiche della classe Array. Vogliamo poter usare l’operatore << per inserire magliette e calzine e forse più avanti, quando faremo le valige vorremmo anche essere in grado di svuotare i nostri cassetti in una valigia
valigia = cassetto_dell_armadio + cassetto_del_comodino
La tentazione è forte.. lo so che è sbagliato, so che Fowler e Booch piangerebbero a vederlo ma.. eccolo qui:
class Cassetto < Array
end
Il cassetto è un Array! Fatto! Finito!
Me ne volete veramente quando l’alternativa sarebbe stata crare un Array dentro Cassetto e poi delegare tutti i metodi che mi servivano, dolorosamente, uno per uno?
Si lo so, purezza del modello di dominio e tutto quanto, ma anche questo codice è doloroso per gli occhi e straripante di ripetizioni che supplicano di essere estratte
class Cassetto
def initialize
@vestiti = []
end
def << vestito
@vestiti << vestito
end
def [] i
@vestiti[i]
end
def first
@vestiti.first
end
...
...
def to_s
@vestiti.join "\n"
end
end
In fondo quando dico
def << vestito
@vestiti << vestito
end
non sto forse dicendo “delega il metodo << al campo @vestiti” ?
E quando dico
def first
@vestiti.first
end
non sto forse dicendo “delega il metodo first al campo @vestiti” ?
Quindi forse, quello di cui abbiamo bisogno è un modo sintetico per esprimere proprio questo concetto di delega, evitando così anche la sporca di ereditare direttamente da Array.
Bene, in ruby possiamo utilizzare direttamente forwardable.rb che ci mette a disposizione def_delegators
class Cassetto
extend Forwardable
def_delegators :@vestiti, :<<, :[], :first, :to_s
def initialize
@vestiti = []
end
def to_s
@vestiti.join "\n"
end
end
cassetto_dell_armadio = Cassetto.new
cassetto_dell_armadio << "calzini"
cassetto_dell_armadio << "maglietta"
cassetto_dell_armadio << "maglietta"
puts cassetto_dell_armadio
puts cassetto_dell_armadio[1]
puts cassetto_dell_armadio.first
Vediamo che estendendo Fordable e usando def_delegators, possiamo dirottare una serie di metodi (<< [] first to_s) su una attributo di classe, che corrisponde all’array contenitore.
Se dovessimo decidere di non mappare i nomi dei metodi uno a uno tra Cassetto e Array posssiamo usare def_delegator, al singolare, aggiungendo:
def_delegator :@vestiti, :size, :numero_vestiti
e potendo quindi invocare:
puts cassetto_dell_armadio.numero_vestiti
Niente gemme, niente librerie aggiuntive, questo è tutto caro semplice vecchio Ruby!