Usare l'inversione del controllo (IoC) per gestire le dipendenze specifiche della piattaforma

Completato

La struttura di progetto tipica di un'applicazione Xamarin.Forms include in genere due o più progetti principali specifici della piattaforma e un progetto di codice condiviso.

Si supponga di avere progetti principali iOS e Android. La libreria di codice condiviso è una libreria .NET Standard. I progetti principali faranno riferimento al progetto di codice condiviso, poiché il loro comportamento dipende dal codice condiviso. Ogni progetto principale determina di quali oggetti vengono create istanze. Ad esempio, il progetto controlla la creazione di istanze della classe dell'applicazione Xamarin.Forms.

Nell'applicazione si vuole usare la finestra di dialogo personalizzata in una pagina ContentPage denominata AboutPage. Si può scegliere di usare l'interfaccia oppure la classe astratta. Entrambe le opzioni consentiranno di implementare il comportamento di ContentPage.

Usare un'interfaccia per implementare una finestra di dialogo personalizzata

Di seguito è riportato un esempio che usa l'interfaccia IMessageDialog.

using System;
using System.Collections.Generic;
using Xamarin.Forms;

namespace MyApplication
{
    public partial class AboutPage : ContentPage
    {
        public AboutPage()
        {
            InitializeComponent();
        }

        void Handle_Clicked(object sender, EventArgs e)
        {
            IMessageDialog messageDialog = null;
            messageDialog.ShowMessage("About", "Some information ...", "Ok");
        }
    }

    public interface IMessageDialog
    {
        void ShowMessage(string title, string message, string buttonText);
    }
}

Il codice precedente verrà compilato senza errori. Si noti, tuttavia, che messageDialog ha un valore null. È necessario decidere di quale classe concreta creare un'istanza, ovvero new MessageDialog_iOS() o new MessageDialog_Android(). Dato che l'applicazione deve essere eseguita in entrambe le piattaforme, si vuole creare un'istanza di entrambe le classi. La classe di cui creare le istanze dipende dalla piattaforma in cui è in esecuzione l'applicazione. La scelta di una classe specifica della piattaforma implica anche che AboutPage ora ha una dipendenza dal codice di piattaforma.

Usare una classe astratta per implementare una finestra di dialogo personalizzata

Di seguito è riportato un esempio che usa una classe astratta.

using System;
using System.Collections.Generic;
using Xamarin.Forms;

namespace MyApplication
    {
    public partial class AboutPage : ContentPage
    {
        public AboutPage()
        {
            InitializeComponent();
        }

        void Handle_Clicked(object sender, EventArgs e)
        {
            MessageDialog messageDialog = null;
            messageDialog.ShowMessage("About", "Some information ...", "Ok");
        }
    }

    public abstract class MessageDialog
    {
        public static Func<MessageDialog> Create { get; set; }
        public abstract void ShowMessage(string title, string message, string buttonText);
    }
}

In questo esempio, la classe MessageDialog ha una nuova proprietà denominata Create. La proprietà Create restituirà un delegato, Func<MessageDialog>. Il delegato è responsabile della creazione di un'istanza concreta di MessageDialog quando viene richiamato. Si noti che la proprietà è definita come statica. Questo è importante, perché indica che è possibile accedere alla proprietà senza un'istanza di MessageDialog.

Anche il codice precedente verrà compilato senza errori. Tuttavia, è comunque necessario scrivere codice specifico della piattaforma per assegnare un'istanza di una classe derivata alla variabile messageDialog. La classe astratta, come l'interfaccia, ha una dipendenza da un elemento specifico della piattaforma.

In entrambi i casi, la classe AboutPage ha un oggetto di cui non può controllare la creazione di istanze. Si ricordi che non è possibile aggiungere un riferimento al progetto principale nella libreria condivisa. Questa operazione causerebbe la presenza di un riferimento circolare nella soluzione.

Che cos'è l'inversione del controllo?

L'inversione del controllo (Inversion of Control, IoC) è uno schema progettuale che descrive lo scenario in cui il controllo sulla creazione delle istanze di un oggetto è responsabilità di un'altra classe.

Gli esempi di codice precedente mostrano che la classe condivisa non controlla la creazione di istanze delle dipendenze. La creazione di istanze delle dipendenze ha luogo nei progetti specifici della piattaforma che fanno riferimento alla libreria condivisa. Ecco perché si parla di inversione del controllo per la libreria condivisa.

Risolvere il problema dell'inversione del controllo

È possibile risolvere il problema dell'inversione del controllo tramite:

  • Lo schema Factory.
  • Lo schema del localizzatore di servizi.
  • Lo schema di inserimento delle dipendenze con un contenitore.

Tenere presente che la scelta di una soluzione spesso dipende da diversi fattori. Considerare la propria esperienza, le dimensioni e la composizione del team di sviluppo e in alcuni casi anche le proprie preferenze.