Introduzione
Un'interfaccia definisce un set di proprietà,
metodi ed eventi che una classe deve implementare per poter
aderire correttamente all'interfaccia stessa. Quando una classe implementa una
certa interfaccia, deve quindi disporre di tutto quello che
l'interfaccia prevede: non deve escludere nulla, altrimenti il nostro codice
.NET non viene compilato. In altri termini, possiamo quindi dire che
l'interfaccia definisce un contratto, ovvero quei membri di una
classe che ci permettono di interagire con essa. L'interfaccia, per definizione,
non contiene codice: sarà compito della classe che la implementa quello di dare
una reale implementazione a proprietà, metodi ed eventi forzati
dall'interfaccia.
.NET stesso fa un uso intensivo delle interfacce: IList, ICollection, IComparer, IFormatter, IEnumerator, etc. Molte delle classi del framework fanno
uso ed implementano queste interfacce. Uno degli scopi di un'interfaccia è
quello di semplificare la lettura del nostro codice, perchè possiamo ad esempio
creare una nostra classe e decidere di renderla compliant rispetto
ad una delle interfacce standard .NET: in questo modo, dovremo dotarla di metodi
standard, che gli sviluppatori .NET sono abituati a maneggiare. Ad esempio, se
una classe Shelf implementa IList, deve
implementare tutti i suoi membri : IsFixedSize, IsReadOnly,
Item, Add(), Clear(), Contains(), IndexOf(), Insert(), Remove(), RemoveAt(). Chi
di voi è attento, noterà che tutti questi membri sono gli stessi previsti, per
esempio, dalla classe ArrayList, oppure dagli Items dei controlli come CheckedListBox, ComboBox e ListView : difatti, tutte queste classi
implementano, guarda caso, anche l'interfaccia IList.
Qualche esempio pratico
Supponiamo di avere una classe
Book implementata in questo modo:
class Book
{
private string _Title;
private string _Author;
private int _Pages;
private bool _InLoan;
#region "Proprietà"
public string Title
{
get { return (_Title); }
set { _Title = value; }
}
public string Author
{
get { return (_Author); }
set { _Author = value; }
}
public int Pages
{
get { return (_Pages); }
set { _Pages = value; }
}
public bool InLoan
{
get { return (_InLoan); }
set { _InLoan = value; }
}
#endregion
}
La classe dispone semplicemente di 4 proprietà:
Title, Author, Pages e
InLoan. Volendo, possiamo definire
un'interfaccia IPaper che specifica
come possiamo interagire con un oggetto della classe Book.
interface IPaper
{
StringCollection GetIndex();
Image GetCover();
bool PutBookmark(int WhichPage);
}
L'interfaccia IPaper prevede 3 metodi:
GetIndex (che ritorna una StringCollection con l'elenco di tutti i capitoli
contenuti nel libro), GetCover (che ritorna l'immagine della copertina) e
PutBookmark (che mette un segnalibro ad una pagina ben precisa). A questo punto,
possiamo dire che la classe Book deve implementare
IPaper, usando la sintassi seguente:
class Book : IPaper
{ ...
...
...
}
Fatto questo, dobbiamo
obbligatoriamente modificare la classe Book
implementando i metodi previsti dall'interfaccia. L'utilizzo delle interfacce
favorisce anche il riutilizzo del codice, o quantomeno una sorta di
razionalizzazione. Se scrivo una classe Magazine, che deriva da
Book, non devo inventare nuovi metodi con nomi nuovi
aggiungendo confusione al codice: Magazine potrebbe implementare anch'essa
IPaper, donando automaticamente una standardizzazione del nostro codice, senza
sforzi particolari.
Una nota importante: è buona norma
non modificare più un'interfaccia dopo aver scritto codice che ne fa uso.
Per loro natura, un'interfaccia ed una classe che la implementa sono legati
con un filo a doppio senso: entrambi questi oggetti hanno
sottoscritto un contratto, che non dovrebbe mai quindi essere modificato.
Immaginate di aggiungere un metodo qualsiasi all'interfaccia
IPaper dopo aver scritto del codice: esso risulterebbe
invalidato ed andrebbe modificato in ogni suo punto per farlo aderire nuovamente
all'interfaccia stessa.
Nei prossimi post...
Basta teoria.
Nel prossimo post, vedremo un po' di
codice C#, qualche classe custom e vedremo come farle aderire alle
interfacce standard contenute nel framework .NET. Inoltre, pian piano, ci
addentreremo a fondo nelle classi messe a disposizione per gestire collection di
dati, scegliendo la migliore e la più adatta in base alle nostre
esigenze.