DesignPatterns

MessageBox dal ViewModel senza violare il pattern MVVM

Nel momento in cui decidiamo di realizzare un’applicazione, ad esempio per WP7, utilizzando il pattern MVVM ma senza adottare uno dei framework a disposizione (vedi MVVMLight,Caliburn Micro, …) ci dobbiamo aspettare di dover risolvere alcuni problemi per garantire sempre e comunque il disaccoppiamento tra UI e logica di presentazione e di business che il pattern stesso ci permette di ottenere.

Uno di questi problemi può essere ad esempio la necessità di visualizzare una MessageBox, quindi un componente della UI, a partire da un metodo del ViewModel corrispondente alla pagina nella quale ci troviamo. Tala problematica può essere risolta implementando un servizio che verrà “iniettato” nel ViewModel e che avrà il compito di visualizzare la MessageBox su richiesta.

Poiché dobbiamo garantire che non ci siano dipendenza strette tra il ViewModel e l’implementazione del servizio in questione, bisogna ragionare in termini di interfacce. Possiamo definire, quindi, un’interfaccia per tale servizio in modo da dare la possibilità di implementarla di volta in volta in maniera diversa; nel nostro caso l’implementazione che realizzeremo farà uso della MessageBox dalla quale prendiamo spunto per definire i metodi dell’interfaccia.

public interface IDialogService
{
    DialogResult Show(string messageText);

    DialogResult Show(string messageText, string caption, DialogButton button);
}

L’interfaccia IDialogService espone gli stessi metodi della classe MessageBox (nella quale però sono statici) mentre DialogResult e DialogButton, definiscono rispettivamente i possibili valori restituiti dalla dialog box a seguito dell’interazione con l’utente ed i bottoni che vogliamo visualizzare al suo interno. Per semplicità, sono stati definiti con due enumerativi uguali a quelli corrispondenti per la classe MessageBox (MessageBoxResult e MessageBoxButton).

public class DialogService : IDialogService
{
    #region IDialogService...

    public DialogResult Show(string messageText)
    {
        MessageBox.Show(messageText);
        return DialogResult.OK;
    }

    public DialogResult Show(string messageText, string caption, DialogButton button)
    {
        return this.MessageBoxToDialogResult(MessageBox.Show(messageText, caption, this.DialogToMessageBoxButton(button)));
    }

    private DialogResult MessageBoxToDialogResult(MessageBoxResult messageBoxResult)
    {
        return (DialogResult)messageBoxResult;
    }

    private MessageBoxButton DialogToMessageBoxButton(DialogButton dialogButton)
    {
        return (MessageBoxButton)dialogButton;
    }

    #endregion
}

L’implementazione DialogService non solo implementa i metodi esposti dall’interfaccia ma utilizza due metodi privati per mappare i DialogResult e DialogButton sui corrispondenti MessageBoxResult e MessageBoxButton. In questo caso, la mappatura è banalmente un cast per come abbiamo definito gli enumerativi; in altri casi potrebbe essere più complessa.

L’utilizzo di questo servizio sarà possibile iniettandolo nel ViewModel attraverso il costruttore.

private IDialogService dialogService;

public ViewModel(IDialogService dialogService)
{
    this.dialogService = dialogService;
}

Infine, sarà utilizzabile direttamente in un qualsiasi metodo del ViewModel.

this.dialogService.Show("Messaggio da visualizzare");

Pattern Singleton con parametri

Attraverso il pattern Singleton, siamo in grado di rendere disponibile un’unica istanza di una classe in un qualunque punto di un’applicazione essa venga referenziata, in quanto l’allocazione dell’oggetto avviene una sola volta in corrispondenza del primo accesso.

L’implementazione più semplice del pattern, senza considerare le problematiche di thread-safety (per le quali vi rimando a questo link), prevede all’interno della classe stessa :

  • un campo privato statico “instance” che rappresenterà l’unica istanza della classe a cui appartiene;
  • un costruttore privato, quindi non invocabile dall’esterno, che istanzia la classe assegnando il riferimento al campo suddetto;
  • un metodo o una proprietà che ritorna il riferimento al campo privato in maniera intelligente, creando l’istanza della classe se non esiste oppure ritornando il suo riferimento se la stessa è stata già allocata;

Poiché poche righe di codice riescono ad esprimersi più di mille parole, riporto di seguito la versione base di implementazione del pattern :

<br />public class MyClass<br />{<br />    private MyClass instance = null;<br />    ...<br />    ...<br />    private MyClass()<br />    {<br />        // costruzione oggetto<br />    }<br />    ...<br />    ...<br />    public MyClass Instance<br />    {<br />        get<br />        {<br />            if (instance == null)<br />                instance = new MyClass();<br />            return instance;<br />        }<br />    }<br />}<br />

Nel caso in cui abbiamo necessità di passare dei parametri in fase di allocazione dell’oggetto, come possiamo comportarci ?

Una possibilità potrebbe essere quella di implementare un metodo GetInstance() in luogo della property Instance e predisporlo con dei parametri in ingresso.

Ad esempio, nel caso di due parametri :

private MyClass(int param1, int param2)
{
    // costruzione oggetto
}

public MyClass GetInstance(int param1, int param2)
{
    if (instance == null)
        instance = new MyClass(param1, param2);
    return instance;
}

Con questa soluzione, si può dar luogo ad un’ambiguità. Basta considerare il seguente esempio :

MyClass a = MyClass.GetInstance(1,2);
MyClass b = MyClass.GetInstance(3,4);

Chi lavora con l’oggetto a, si aspetta di aver inizializzato correttamente l’oggetto con i valori 1 e 2. Chi lavora con l’oggetto b, invece, si aspetta di avere un oggetto il cui stato interno ha valori 3 e 4. Ovviamente, la prima inizializzazione prevarrà sulla successiva e quindi sia il riferimento a che b punteranno al medesimo oggetto avente stato con valori 1 e 2 (positivo per a ma non per b).

Una soluzione a questo problema può essere quello di implementare il pattern nel modo seguente :

public class MyClass
{
    private MyClass instance = null;
    ...
    ...
    private MyClass(int param1, int param2)
    {
        // costruzione oggetto
    }

    public MyClass GetInstance()
    {
        if (instance == null)
            throw new InvalidOperationException("MyClass instance not created !");
        return instance;
    }

    public static MyClass Create(int param1, int param2)
    {
        if (instance != null)
            throw new InvalidOperationException("MyClass instance already created !");

        instance = new MyClass(param1, param2);
        return instance;
    }
}

In pratica, viene fornito all’esterno un metodo statico di Create() per la prima allocazione dell’oggetto ed il metodo GetInstance() per ricavarne l’istanza corrente. Una doppia chiamate al metodo Create() solleverebbe l’eccezione “oggetto già creato” così come una chiamata alla GetInstance() senza aver prima creato l’oggetto, solleverebbe l’eccezione “oggetto non creato”.