domenica, settembre 02, 2007

KentaIoC - A js IOC library


English version)



Ultimamente a lavoro sto usando un framework IOC

e fin da subito ho capito le potenzialità di un simile approccio, il quale permette
di separare concettualmente le proprie classi.
Per fare ciò ci si basa su un unico concetto, separare la logica che lega le classi esternamente.

Mentre nella programmazione tradizionale spesso accade che un oggetto A all' interno di un metodo configuri un oggetto B prima di invocare qualche metodo di ques'ultimo,
con l' Inversion of Control A non dovrà mai configurare B ma invocarlo direttamente.

probabilmente mi sono spiegato male e invito chi desiderasse capire meglio il concetto ad approfondire in maniera teorica l' argomento altrove in quanto ora mi concentrerò in maniera pragmatica nella scrittura di un codice che permetta l'utilizzo della IOC in javascript.



In che modo si mette in piedi l'IOC?
normalmente avremo una classe A che contiene al suo interno un membro B
che estende un interfaccia nota, in questo modo anche se A non conosce l'effettiva implementazione di B può dialogare con B poichè tra A e B c'è in piedi un contratto definito dall' interfaccia.
Si, ma in javascript non esiste il concetto di interfaccie, quindi la programmazione attraverso interfaccie non si può usare!
Ovviamente questo è il primo problema da affrontare, e a questo aggiungo che JS è un linguaggio dinamico e non ha lo Strict Type (non è un linguaggio fortemente tipizzato), anche se Andr3a sicuramente mi smentirà. volendo sarebbe possibile simulare le interfaccie in JS ma questo andrebbe contro il concetto KISS, quindi cercando un alternativa userò un concetto che nonostante non c'entri nulla con le interfaccie permette di lavorare lo stesso con questa metodologia.
Questo concetto che andrò a introdurre è il Duck typing,
ovvero se la nostra classe si muove come un fagiano e ha le ali di un fagiano allora per noi è un fagiano, anche se in realtà è un aquila.


Al pari con la IOC c'è un altro concetto, quello della Dependency Injection
che in pratica dice che un oggetto C che non conosce ne A ne B si occuperà di mettere "il giusto B" all' interno di A
C è inoltre il nostro Container.
Cosa fà esattamente un container?
Un container mantiene al suo interno una tabella che indica in maniera univoca un istanza di un oggetto(normalmente sono dei componenti, ovvero dei mini oggetti che fanno solo una cosa, ma la fanno bene) associandogli un identificativo.
Si, vabbè, ma non è ora di far vedere un po di codice?
Ecco come si può gestire facilmente un container.


var c={};
//configuration of c
c.B={getMessage:function(){return "test Container"}}

//A doesn't know exactly B it only know that B have getMessage(duck typing approach)
var A = {
B:c.B,//A doesn't have the implementation of B,it rely on C to have the correct one.
test:function(){alert(this.B.getMessage())}
}

A.test();

Ma da quel che ho capito C è solo un hashtable?
No, in realtà sto introducendo un concetto alla volta, se è vero che C è un hashtable
a tutti gli effetti, per avere una corretta gestione della IoC il container deve fare altro.
In base alla configurazione, il container deve poter istanziare il corretto oggetto (Factory)
ed è qui che entra in azione l'injection, il nostro container deve poter iniettare l'oggetto all' interno di un altro oggetto.
I vari framework hanno varie metodologie per iniettare le dipendenze, sicuramente le 2 più usate e che troverete in qualsiasi framework sono l'iniezione per costruttore e per setter.
per semplificare le cose il mio script dipende dall' extend script di andrea giammarchi
, assicuratevi quindi di includerlo in testa al vostro script

var A ={
test:function(){alert(this.B.getMessage())}//duck typing
};

var container = {
C : {},
register:function (id,component){
this.C[id]=component;
},
find: function (id){
return this.C[id];
},
bySetter: function(){
var id=arguments[0];

var Obj = this.find(id);

for(var i=1,max=arguments.length;i<max;i++ ){
Obj = Obj.extend(arguments[i]);

}

return Obj;

},
byConstructor : function(){

var id = arguments[0];
var pars = new Array();
for(var i=1,max=arguments.length;i<max;i++ )
pars.push(arguments[i]);


var Obj=this.find(id);
return Obj.apply(Obj,pars);


}

}

/*Start setter configuration*/
container.register('AS',A);

container.register('BS',{getMessage:function(){return 'Hello setter injection!'}});
container.register('BS2',{getMessage:function(){return 'Hello setter injection #2!'}});



container.bySetter('AS',{'B':container.find('BS')}).test();
container.bySetter('AS',{'B':container.find('BS2')}).test();


/*Constructor injection example*/
function AC(B){

return {
test:function(){alert(B.getMessage())}
}
}

container.register('BC',{getMessage:function(){return 'Hello constructor injection '}});
container.register('AC',AC);


container.byConstructor('AC',container.find('BC')).test();

La parte riutilizzabile dello script sopra è il container che ho scritto.
Il suddetto container permette di registrare i componenti usando register(id,component)
di cercare i componenti già registrati find(id)
permette infine di iniettare per costruttore byConstructor (id,parameters)
e di iniettare le dipendenze tramite setters bySetter(id,setterObject)


Grazie ad Andrea Giammarchi potete scaricare il codice da qui : http://www.devpro.it/code/164.html

Nessun commento: