MVVM simplement – 4ème partie

Dans l’article précédent, j’ai montré comment implémenter MVVM et particulièrement comment découpler la View du ViewModel grâce à l’inversion de contrôle.

Je vais utiliser à nouveau ce concept pour dissocier les traitements métier de l’application du ViewModel. Actuellement, les traitements liés à l’accès aux données et au calcul de l’IMC sont implémentés dans le ViewModel.

Je vais les isoler dans une partie dite de “Service” qui fait partie de la couche Model mais permet de séparer les traitements des données manipulées. Donc, je commence par créer un dossier “Service”.

Je crée la classe DataService qui permet de charger et de sauvegarder les données de l’application. Au passage, je crée 2 méthodes génériques qui peuvent manipuler n’importe quel type de données.

 public class DataService : IDataService
    {
        public void SaveData<T>(T data, string fileName)
        {
            var xs = new XmlSerializer(typeof(T));
            using (var wr = new StreamWriter(fileName))
            {
                xs.Serialize(wr, data);
            }
        }
 
        public T LoadData<T>(string fileName) where T : class
        {
            T data = null;
 
            var xs = new XmlSerializer(typeof(T));
 
            if (!File.Exists(fileName))
                return null;
 
            using (var rd = new StreamReader(fileName))
            {
                data = xs.Deserialize(rd) as T;
            }
            return data;
        }
    }

Pour cette classe, je crée l’interface qui correspond.

public interface IDataService
    {
        void SaveData<T>(T data, string fileName);
        T LoadData<T>(string fileName) where T : class;
    }

Pour utiliser ces méthodes, je vais tout d’abord enregistrer le type dans le composant d’inversion de contrôle, ceci afin de conserver un couplage faible entre le ViewModel et le Model.

  public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            SimpleIoc.Default.Register<DataService>();
            
            SimpleIoc.Default.Register<MainViewModel>();
        }

Ensuite, je définie une variable qui va recevoir une instance du service dans le ViewModel

private readonly IDataService _dataService;


J’obtiens une instance de ce type

_dataService = SimpleIoc.Default.GetInstance<DataService>();


Et enfin je peux l’utiliser comme ici avec la méthode LoadData

  public T LoadData<T>(string fileName) where T : class
        {
            T data = null;
 
            var xs = new XmlSerializer(typeof(T));
 
            if (!File.Exists(fileName))
                return null;
 
            using (var rd = new StreamReader(fileName))
            {
                data = xs.Deserialize(rd) as T;
            }
            return data;
        }
    }


Il me reste à créer un service que j’ai nommé “QuantifiedSelf” (concept très à la mode) dans lequel je vais implémenter la méthode de calcul d’IMC. Je crée également l’interface qui correspond à cette classe.

 public class QuantifiedSelfService : IQuantifiedSelfService
    {
        double IQuantifiedSelfService.CalcBmi(double height, double weight)
        {
            return height > 0 ? weight/Math.Pow(height, 2) : 0;
        }
    }

Je l’enregistre mais cette-fois ci j’utilise pour l’exemple une signature différente de la méthode Register qui prend en type générique l’interface et le type.

SimpleIoc.Default.Register<IQuantifiedSelfService, QuantifiedSelfService>();


Ici je vais utiliser l’injection de dépendance par le constructeur, c’est-à-dire que l’inversion de contrôle va chercher la dépendance sur le constructeur du MainViewModel  et la fournir.

private readonly IQuantifiedSelfService _quantifiedSelfService;
        
        public MainViewModel(IQuantifiedSelfService quantifiedSelfService)
        {
            _quantifiedSelfService = quantifiedSelfService;

Attention, surtout ne pas utiliser directement la référence fourniemais affecter une variable du même type dans le ViewModel, il s’agit en effet d’une référence faible (WeakReference) qui peut être supprimée à tout moment par le garbage collector.

Pour finir, j’utilise la méthode de mon service pour calculer l’IMC.

CalcBmiCommand = new RelayCommand(() =>
            {
                if (CurrentPerson != null)
                {
                    CurrentPerson.Bmi = _quantifiedSelfService.CalcBmi(CurrentPerson.Height, CurrentPerson.Weight);
                    UpdatePerson();
                }
            });

Le code source est ici. Cette série d’articles permettant de débuter en MVVM est terminée.

December 30, 2017

Tags: ,

Leave a Reply

Your email address will not be published. Required fields are marked *