di .NET e di altre amenità

Marker Interfaces

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

Commenti

2005-07-04T07.41.00+01.00 #

Un altro esempio di marker interfaces (non sapevo si chiamassero cosi' Smile ) è per esempio l'interfaccia INamingContainer che si utilizza nello sviluppo di controlli web e serve al "motore" di asp.net per poter creare controlli univoci nelle pagine.

Luca Mauri

2005-07-04T09.03.00+01.00 #

E' una questione di scelte... Io tendenzialmente preferisco implementare un attributo personalizzato a meno che non vi siano poi dei metodi che accettano SOLO oggetti "unknown". Nel caso specifico del pattern SpecialCase però avrei implementato INonExistentImhoObject su tutti gli oggetti e avrei messo un metodo IsUnknown. Secondo me é più pulita perché altrimenti tutti i tuoi IMHO Objects dovranno essere ereditabili e potrebbe essere una scelta di cui pentirsene in un secondo momento.
Comunque ribadisco: questione di scelte ovviamente...

Michele Bernardi

2005-07-04T09.24.00+01.00 #

Io i autodefinisco il prosaico delle graffe: sono talmente un cultore dell'indentazione a tutti i costi e della logorroicità del codice a tutti i costi che le graffe le metto sempre anche su un semplice if che ha una sola istruzione. Non solo arrivo pure a non usare _mai_ l'operatore di negazione "!", nelle mie espressioni condizionali uso sempre "== false" o "== true".
Dici che sono malato? Smile
saluti

Roberto Messora

2005-07-04T12.24.00+01.00 #

Ciao Andrea, se ho ben capito una "marker interface" è un'interfaccia senza membri che può essere utilizzata per dichiarare che una certa classe possiede una qualche caratteristica.
Non si può ottenere la stessa cosa assegnando alla classe un opportuno attributo di classe? (come [Serializable] per la classe Hashtable, tanto per capirci).
Spero che la mia domanda non sia del tutto campata per aria, ciao.

Federico Zanin

2005-07-04T12.35.00+01.00 #

Si federico, è vero è possibile anche usare la reflection per ottenere il medesimo risultato. I metodi sono molti, come ad esempio anche usare un aproprietà vera e propria. Tuttavia nel caso della reflection via codice occorre estrarre i customattributes per verificare la presenza. Questo introduce un overhead molto maggiore di quello che si potrebbe ottenere con l'uso dell'operatore "is".

codeblock

Aggiungi commento




  Country flag

biuquote
  • Commento
  • Anteprima
Loading