Un piccolo serpente ci conduce attraverso le principali caratteristiche del Windows Phone 7
Introduzione Core UI & Design Conclusione Premi Discussioni
Marcosroom.it Didatticando

IoC (Inversione di Controllo)

Apriamo una parentesi sull'IoC:

(

Vediamo ciò che dice Wikipedia:

"Per Inversion of Control (IOC - inversione di controllo) si intende un pattern di programmazione relativamente nuovo, che ha avuto presa sulla comunità dei programmatori grazie al framework Spring, secondo il quale si tende a tener disaccoppiati i singoli componenti di un sistema, in cui le eventuali dipendenze non vengono scritte all'interno del componente stesso, ma gli vengono iniettate dall'esterno: non si segue il normale flusso di controllo dei linguaggi imperativi, in cui, nel momento del bisogno, si richiamano funzioni di classi o librerie esterne, gli oggetti quindi non istanziano e richiamano gli oggetti dal quale il loro lavoro dipende, ma queste funzionalità vengono fornite da un ambiente esterno tramite dei contratti definiti da entrambe le entità."

Penso che sia meglio che lo traduciamo in un linguaggio umano...

Iniziamo con un esempio: dobbiamo costruire un oggetto in grado di estrarre il contenuto di un tag di una pagina HTML. Ognuno di noi scriverebbe:

public class TagGetter
{
    public string GetContent(string pageUrl, string tagName)
    {

        //Downloads the page
        string page = new System.Net.WebClient().DownloadString(pageUrl);

        //Gets the tag
        string tagContent =
            System.Text.RegularExpressions.Regex.Match(page, "<" + tagName + ">[^>]*</" + tagName + ">").Value;
        tagContent = tagContent.Substring(tagName.Length + 2, tagContent.Length - 2 * (tagName.Length + 2) - 1);

        //Returns
        return tagContent;

    }
}
        

Ora analizziamo ciò che questo oggetto fa: innanzitutto, scarica la pagina utilizzando la classe System.Net.WebClient, poi estrae il tag utilizzando le Regex, e infine restituisce il valore trovato. Forse, ti stai chiedendo cosa ci sia di sbagliato in questo pezzo di codice, e ti rispondo dicendo: nulla. Oggettivamente, questa classe è perfetta: scarica e estrae il tag correttamente, e se provi ad estrarre il tag "title" della homepage di google, restituisce "Google". Ma, prova a vedere questa classe da un punto di vista più generico: ci sono 2 errori principali, i quali richiedono un approccio differente per essere risolti.

  1. Questa classe fa troppe cose! Un buon principio di design è la SoC (Separation of Concerns, separazione dei concetti). Secondo questo principio, un singolo oggetto deve fare una sola cosa e farla bene. Dovremmo quindi dividere questa classe in altri due oggetti: uno per scaricare la pagina, e l'altro per estrarre i tag.
  2. Cosa succederebbe se il documento cercato non fosse raggiungibile tramite il protocollo HTTP? Dovremmo cambiare il corpo del metodo aggiungendo (o sostituendo) questa caratteristica con un'altra, come FTP. Il processo di estrazione del tag può essere migliorato, ma anche questo comporterebbe il cambiamento del corpo del metodo. So che tutto questo potrebbe apparire insensato, ma prova ad immaginare una situazione particolare, in cui questo approccio condurrebbe ad un miglioramento di performance. In poche parole, l'oggetto TagGetter he una conoscenza troppo profonda del meccanismo globale, e questo non va bene, poichè conduce ad un errata architettura dell'applicazione.

Poichè ho intenzione di risolvere questi problemi utilizzando Castle.Windsor (vedremo dopo cos'è), devo utilizzare le stesse parole della sua documentazione per spiegare i concetti di componente e servizio:

"Un componente è una piccola unità di codice riutilzzabile. Dovrebbe implementare ed esporre un solo servizio, e farlo bene. In pratica, un componente è una classe che implementa un servizio (interfaccia). L'interfaccia è il contratto del servizio, che crea un livello di astrazione in modo tale che sia possibile sostituire l'implementazione del servizio senza fatica."

Ora che sappiamo cosa sono componenti e servizi, possiamo andare direttamente alla soluzione del problema

La classe TagGetter fa più del dovuto, e dobbiamo dividerla: i due compiti di questo oggetto sono molto generici e possono essere svolti in diversi modi, quindi, dobbiamo creare un servizio (interfaccia) che definisca le azioni che un oggetto può compiere, senza scriverne l'implementazione concreta (componente). Ecco le due interfacce, una per scaricare la pagina, e l'altra per estrarre il contenuto di un tag.

public interface IPageDownloader {
    string Download(string url);
}

public interface ITagExtrator {
    string Extract(string content, string tagName);
}
    

Ora dovremmo scrivere le implementazioni concrete di quete interfacce:

public class PageDownloader : IPageDownloader {
    public string Download(string url) {
        return new System.Net.WebClient().DownloadString(url);
    }
}

public class TagExtractor : ITagExtrator {
    public string Extract(string content, string tagName) {
        string tagContent = 
            System.Text.RegularExpressions.Regex.Match(content, "<" + tagName + ">[^>]*</" + tagName + ">").Value;
        tagContent = 
            tagContent.Substring(tagName.Length + 2, tagContent.Length - 2 * (tagName.Length + 2) - 1);
        return tagContent;
    }
}
    

Uhm... qui sorge un piccolo problema... come passiamo questi oggetti alla classe TagGetter? Semplice: cambiamo il costruttore di questa classe per accettare parametri di tipo IPageDownloader e ITagExtractor, i quali verranno memorizzati in alcune variabili. Ecco il nuovo codice:

public class TagGetter
{
    //Stored objects
    private IPageDownloader _pageDownloader;
    private ITagExtrator _tagExtractor;

    public TagGetter(IPageDownloader pageDownloader, ITagExtrator tagExtractor)
    {
        //Stores the objects
        _pageDownloader = pageDownloader;
        _tagExtractor = tagExtractor;
    }

    public string GetContent(string url, string tagName)
    {
        //Downloads the page
        string page = _pageDownloader.Download(url);

        //Gets the tag
        string tagContent = _tagExtractor.Extract(page, tagName);

        //Returns
        return tagContent;
    }
}
        

Come puoi notare, il codice del metodo TagGetter.GetContent() è più semplice e chiaro, e non di preoccupa di come scaricare la pagina o estrarre il tag: lo faranno le implementazioni delle interfacce! In questo modo, possiamo facilmente cambiare il componente per il download o quello per l'estrazione, senza cambiare la classe TagGetter! Inoltre, possiamo facilmente riutilizzare il singolo componente in un'altra applicazione o possiamo scrivere test specifici su di esso.

Comunque, c'è uno svantaggio; per chiamare questo metodo dovremmo scrivere:

string title = new TagGetter(new PageDownloader(), new TagExtractor()).GetContent("http://www.google.it", "title");

Questo non è poi così male, ma per me è troppo lungo e complesso. Qui è dove Castle.Windsor ci viene in soccorso. In poche parole, Castle.Windsor è un contenitore che puoi configurare per contenere degli oggetti, e che potrai riprendere dopo, in base alle necessità. è come una grande scatola che contine degli oggetti; su di essa c'è un indice sul quale vengono accoppiati servizi e componenti. Quando hai bisogno, quindi, di un componente per il download di pagine web, cerchi un IPageDownloader e trovi un'istanza della classe PageDownloader. Un esempio potrebbe essere più esplicativo della spiegazione stessa:

//Creates a new container
WindsorContainer container = new WindsorContainer();

//Registers the downloader
container.Register(Component
                   .For<IPageDownloader>()
                   .ImplementedBy<PageDownloader>());

//Registers the tag extractor
container.Register(Component
                   .For<ITagExtrator>()
                   .ImplementedBy<TagExtractor>());


//Registers the tag getter
container.Register(Component
                   .For<TagGetter>());

//Gets the tag getter
TagGetter getter = container.Resolve<TagGetter>();
            
//Calls the method
string title = getter.GetContent("http://www.google.it", "title");
        

Come puoi notare, le prime 3 chiamate a Register() vengono utilizzate per registrare le interfacce e le loro implementazioni concrete (il contenitore può essere configurato anche con un file XML), ma la cosa più interessante è questa: la chiamata a Resolve() restituisce un'istanza della classe TagGetter. Ma se ricordi, questa classe ha un costruttore con 2 parametri, quindi, cosa è successo? Quando viene chiamato il metodo Resolve(), il WindsorContainer controlla i parametri del costruttore e, se c'è un componente o un servizio registrato compatibile con il tipo del parametro, il contenitore crea automaticamente l'istanza, passando i parametri corretti al costruttore (secondo la configurazione) e quindi, il gioco è fatto!

Questa è solo una parentesi sull'IoC, e non ho mostrato nemmeno la più piccola parte di cosa possiamo fare con l'IoC o cosa Castle.Windsor è in grado di fare, quindi chiudo questo paragrafo con questi riferimenti:

) - Non ho dimenticato la parentesi aperta all'inizio del paragrafo!

 

Condividi
Indietro Tutti i webmaster che volessero segnalare, non copiare,
il contenuto di questa pagina sul proprio sito, possono farlo liberamente.
E' gradito un preavviso tramite mail all'autore e l'iserimento,
nella pagina di citazione, di un link verso la pagina corrente.
© Copyright    Marco's Room
Avanti
Download SnakeMobile

Scaricato 115 volte

Celle IoC (Inversione di controllo) Un contenitore per Windows Phone 7 Controller di movimento Obiettivi Livelli Serpenti nemici Salvataggio e caricamento dei livelli