Non ho mai sentito parlare su UgiDotNet di markers interfaces,
non so se dipenda dal fatto che vi sono iscritto da relativamente poco tempo, o
se proprio l'argomento non è mai stato toccato. Stasera, durante una sessione di
sviluppo di IMHO 2.0 mi sono trovato di fronte all'esigenza di usarle per
risolvere con eleganza un problema che mi si è presentato, perciò ho deciso di
proporvi in questo breve post qualche spezzone di codice per illustrarvene
l'utilità.
Innanzitutto vediamo di spiegare in due parole che cosa intendo
quando parlo di markers interfaces. Chiunque abbia un po' di dimestichezza con
la programmazione ad oggetti sa bene cosa sia un'interfaccia. Tipicamente nei
libri viene definita come un contratto che stabilisce quali
metodi dovranno contenere le classi che la implementano. In C#, e
genericamente nel framework .NET una classe può implementare più di una
interfaccia e questo permette di realizzare il polimorfismo delle classi. Non
voglio ora dilungarmi in esempi in merito, ma darò per scontato che l'argomento
sia ormai ben conosciuto. Ora vi pongo una domanda: cosa succede se dichiaro una
interfaccia senza alcun metodo? Semplicemente nulla. Il compilatore non
solleverà nemmeno uno warning per farci notare la bizzarria. Di primo
acchito si potrebbe dire che una interfaccia senza metodi esposti non serve
veramente a nulla, se non che, andando a spulciare l'SDK del framework si
scoprirà che di interfacce di questo tipo ve ne sono e anche parecchie. Provate
ad esempio a vedere la IReadOnlySessionState. Tale interfaccia è definita una
marker interface e verrà implementata dalla classe che il compilatore crea
quando compila una pagina aspx, se nella direttiva Page viene specificato
EnableSessionState="ReadOnly". Questo è un modo rapido ed elegante di
trasformare una proprietà in un comportamento. Usando un marker, nel codice si
potrà fare uso dell'operatore "is" per verificare se la classe implementa
l'interfaccia ed agire di conseguanza.
Ma visto che un esempio vale mille parole, eccovi come ho
impiegato io una marker interface. Uno dei problemi tipici della programmazione
ad oggetti è il mapping tra il mondo relazionale e quello oop. In IMHO 2.0 ho
deciso di non adottare un framework di persistenza che si occupasse anche di
questa problematica perchè in realtà non posso direche ne esistano di veramente
maturi e per una questione di licenze nemmeno di praticamente usabili nel mio
caso. Perciò mi sono "arrangiato" implementando un po' di patterns. Innanzitutto
ho creato una Factory cui passo il tipo di oggetto da create è il DataReader da
cui attingere:
public static ImhoObject Create(
Type type,
IDataReader reader)
{
if (type==typeof(User))
return CreateUser(reader);
else if (type==typeof(Role))
return CreateRole(reader);
throw new ApplicationException("Unknown type");
}
private static User CreateUser( IDataReader reader )
{
if ( reader.Read() )
{
User
user = newUser();
// omissis: popolo l'oggetto con il reader...
return user;
}
return new UnknownUser();
}
Questo metodo è stato creato per poter estrarre da un reader un
singolo oggetto. In questo modo è possibile riutilizzarlo sia nel caso di una
lettura singola, sia nel caso di un reader contenente molti oggetti. Analizzando
per bene il codice si vede anche che ho
implementato il pattern SpeciaCase infatti nel caso in cui il
reader non contenga alcun oggetto restituisco un'istanza di UnknownUser, una
classe che eredita da User e che rappresenta il caso in cui un utente non
esiste. A questo punto mi sono trovato nella necessità di creare un metodo che
dato un reader lo scandisca da cima a fondo e restituisca un array
di oggetti estratti da esso. Il problema risiedeva nel fatto che creando
tale metodo, in modo che facesse uso al suo interno del metodo Create(), mi
trovavo nella necessità di testare l'oggetto restituito e nel caso in cui
esso fosse UnknownUser uscire dal loop. Però così facendo mi sarei anche trovato
nella necessità di creare un metodo ad-hoc per ogni classe di oggetto, User,
Role, Weblog, e cosi' via. Ho così immaginato che per ognuno di essi sarebbe
esistito l'Unknown e preso atto del fatto che avevo predisposto una
radice della gerarchia di oggetti in ImhoObject, ho deciso di marcare le classi
di tipo Unknown con una interfaccia INonExistentImhoObject in questo
modo:
[Serializable]
public class UnknownUser :
User, INotExistentImhoObject
{
public UnknownUser()
{}
}
Così facendo mi sono finalmente potuto creare un metodo
completamente generico che sfruttasse l'interfaccia marker per gestire il
raggiungimento della fine del reader:
public static Array CreateArray(
Type type,
IDataReader reader )
{
ArrayList list = new ArrayList();
ImhoObject item;
while( !( ( item = Create( type, reader ) )
is INotExistentImhoObject ) )
list.Add( item );
return list.ToArray( type ) as Array;
}
Et-voilà il gioco è fatto. Il metodo è del tutto generico tanto che
basta passare il tipo di oggetto e il reader e puntualmente ritornerà un array
contenente gli oggetti richiesti. Solo un rilievo. La condzione
del ciclo while è scritta come piace a me, in puro stile "C", so che per
molti risulterà antipatica, perchè soffre un po' di scarsa leggibilità, ma
volete mettere: due righe e nemmeno una parentesi
graffa!
powered by IMHO 1.2