Comprendre la structure de l’application Reversi

Applies to Windows only

L’exemple Reversi utilise une structure en couches appelée modèle MVVM (Model-View-ViewModel). Cette structure sépare l’interface utilisateur du code non-IU, ce qui simplifie le débogage, les tests et les développements futurs. Cette rubrique décrit chaque couche de l’exemple, les parties dans chaque couche, et la façon dont les parties fonctionnent ensemble.

L’exemple Reversi se différencie des exemples XAML plus simples dont la majeure partie du code non-XAML se trouve dans des fichiers code-behind. Reversi ajoute du code aux fichiers code-behind dans certains cas, mais déplace la plupart du code non-XAML vers d’autres couches et utilise la liaison de données pour connecter ces couches à l’interface utilisateur.

L’exemple Reversi diffère également des exemples XAML plus complexes qui utilisent des techniques plus avancées, y compris des cadres MVVM distincts. Ces cadres sont utiles pour les applications de plus en plus complexes, mais l’exemple Reversi montre les avantages d’une structure simple en couches, construite à l’aide de XAML standard.

Pour une brève présentation du modèle MVVM, voir Utiliser le modèle Model-View-ViewModel (MVVM).

Cette rubrique part du principe que vous connaissez déjà XAML et C#. Si ces technologies ne vous sont pas familières, voir Créer votre première application du Windows Store en C# ou Visual Basic. Si vous comprenez le langage C++, vous pouvez également tirer profit de la version C++ du moteur de jeu Reversi, bien que cela ne soit pas obligatoire pour comprendre le reste de l’exemple.

Pour obtenir une introduction générale à l’exemple, voir Reversi, un jeu du Windows Store en XAML, C# et C++. Pour découvrir comment les fonctionnalités spécifiques sont utilisées dans cet exemple, voir Découvrir comment l’exemple Reversi utilise les fonctionnalités d’application du Windows Store. Pour découvrir comment le moteur de jeu C# d’origine a été porté vers C++, voir Découvrir le moteur de jeu C++ Reversi.

Téléchargez l’exemple d’application Reversi ou parcourez le code source.

Couches Application

L’application Reversi est divisée dans les couches suivantes :

  • La couche modèle inclut le code de base du jeu qui représente les règles du jeu, l’état du jeu et l’intelligence artificielle (IA) ; il est complètement indépendant de l’interface utilisateur. La plupart du code de modèle se trouve dans le projet ReversiGameComponentCS pour la version C# et dans le projet ReversiGameComponentCPP pour la version C++ du moteur de jeu.
  • La couche affichage est implémentée par le dossier Views dans le projet Reversi. Ce code fournit l’interface utilisateur du jeu, qui est définie en XAML avec une large utilisation de la liaison de données.
  • La couche modèle d’affichage est implémentée par le dossier ViewModels dans le projet Reversi. Ce code fournit les cibles de liaison de l’interface utilisateur et gère toutes les interactions de l’utilisateur, mais sinon est complètement indépendant de l’interface utilisateur.

La couche modèle

L’exemple Reversi comprend deux implémentations différentes de la couche modèle :

  • La version C# d’origine se trouve dans le projet ReversiGameComponentCS.
  • La version C++ aux performances améliorées se trouve dans le projet ReversiGameComponentCPP.

Il existe aussi un projet ReversiGameModel qui contient des interfaces et des classes de prise en charge qui servent à découpler l’application et les tests unitaires de l’implémentation des composants du jeu. Les projets Reversi et de tests unitaires ont des dépendances uniquement envers ces interfaces et classes de prise en charge. Ils n’ont aucune dépendance directe envers les implémentations des composants du jeu.

Le projet ReversiGameComponentCS implémente l’interface du modèle de jeu directement. Le projet ReversiGameComponentCPP, en revanche, ne peut pas mettre en œuvre d’interfaces .NET. Par conséquent, le projet Reversi comprend une classe CLRSerializableCPPGame dans le dossier Models. Cette classe met en œuvre l’interface de jeu et fournit un wrapper .NET autour du composant C++. De cette manière, les tests unitaires et l’application principale consomment le composant C# ou C++, y compris la sérialisation des objets de jeu sur disque en cas de suspension de l’application.

Bien entendu, l’application n’est pas conçue pour utiliser les deux implémentations du moteur de jeu simultanément. Au lieu de cela, elle crée des instances du jeu à l’aide de la classe GameFactory dans le dossier Models. Cette classe crée et renvoie une instance de composant C# ou C++, selon que la constante de compilateur CSHARP est définie ou non.

Les deux versions de la couche modèle sont mises en œuvre en tant que projets Windows Runtime Component. Chaque version du composant comprend la logique de base du jeu, à savoir tout le nécessaire pour représenter les règles du jeu, l’évolution de l’état du jeu réel en cours et l’intelligence artificielle du jeu.

Placer la logique de base du jeu dans un projet Windows Runtime Component distinct est utile pour plusieurs raisons, en plus d’autoriser la création d’une version distincte écrite en C++. Vous pouvez par exemple générer un projet de composant de jeu, puis y faire référence comme assembly de bibliothèque dans une nouvelle solution d’application. De cette façon, vous pouvez remplacer l’interface utilisateur (peut-être en guise d’exercice d’apprentissage) sans changer quoi que ce soit d’autre. Vous pouvez même utiliser le composant dans une application JavaScript.

Autre avantage lié à l’utilisation d’une couche séparée : le code de base du jeu reste aussi simple que possible. La création d’une application de jeu de plateau complète est beaucoup plus difficile que la simple écriture du minimum de code possible pour les règles d’un jeu de société. Toutefois, même le code de règles du jeu simples peut s’avérer encore assez complexe, donc séparer l’interface utilisateur de ce code facilite beaucoup l’écriture et les tests.

Pour plus d’informations, voir Utilisation d’un composant Windows Runtime dans les scénarios de fonctionnalités Reversi.

La couche affichage

La couche affichage se trouve dans le dossier Views du projet Reversi et inclut le code des principales parties visuelles de l’application, y compris chaque page, les menus volants de paramètres et les contrôles du jeu.

La plupart de ces parties comportent des fichiers XAML et des fichiers code-behind, mais dans certains cas, le code-behind est simplement le code réutilisable généré par le modèle d’application Microsoft Visual Studio. La plupart du code qui normalement serait dans les fichiers code-behind se trouve plutôt dans les couches modèle et modèle d’affichage.

Les fichiers XAML contiennent la plupart du code d’affichage. Ces fichiers comprennent principalement l’utilisation de l’Extension de balisage Binding XAML pour définir des liaisons de données entre le code XAML et les modèles d’affichage.

Certains affichages utilisent aussi du code-behind pour effectuer des tâches qui sont plus difficiles à implémenter en XAML et inadaptées aux couches modèle et modèle d’affichage. Quelques scénarios :

  • Configuration spéciale du contexte de données ou des liaisons de données des affichages.
  • Appel de méthodes d’interface utilisateur dans l’API XAML, comme les méthodes de navigation et d’animation.
  • Gestion des événements de l’interface utilisateur pour appeler les méthodes du modèle d’affichage lorsqu’il n’y a pas d’alternative XAML pratique.

Lorsque votre application devient plus complexe, vous pouvez trouver utile de décomposer ces tâches en composantes ou couches plus nombreuses, ou d’utiliser un des nombreux cadres MVVM qui fournissent des services connexes.

Pour savoir comment les vues sont connectées aux modèles d’affichage, voir Liaison de données dans Scénarios de fonctionnalité Reversi.

Pages

Reversi comprend trois pages :

  • StartPage affiche les options de jeu et d’aide affichées au premier démarrage de l’application.
  • GamePage affiche un contrôle PlayerStatus et un contrôle Board complet des contrôles BoardSpace.
  • HelpPage affiche les instructions du jeu, qui comprennent un statut du joueur PlayerStatus et un tableau Board à des fins de démonstration et d’illustration.
Superposition des captures d’écran des trois pages de Reversi

Ces pages ont été créées à l’aide du modèle Page de base de la boîte de dialogue Projet -> Ajouter un nouvel élément de Visual Studio.

Contrôles utilisateur et contrôles personnalisés

La complexité principale de l’interface utilisateur réside dans les contrôles qui constituent le plateau de jeu, le tableau de score et l’horloge. Il s’agit de contrôles utilisateur et de contrôles personnalisés. L’interface utilisateur de ces contrôles, comprenant certaines transitions d’état animées, est définie dans le code XAML. Les contrôles gèrent les transitions à l’aide de propriétés de dépendance personnalisées qui sont liées aux données des propriétés du modèle d’affichage. Pour plus d’informations, voir Liaison de données et Propriétés de dépendance personnalisées dans Scénarios de fonctionnalité Reversi.

  • Board représente le plateau du jeu. Il s’agit d’une sous-classe de UserControl qui affiche un panneau Grid contenant des contrôles BoardSpace et les différents messages du jeu (jeu en pause, déplacement de passe et fin de partie). Board est un contrôle utilisateur car ce type de contrôle permet de facilement définir une partie simple de l’interface utilisateur et ses fonctionnalités dans une paire XAML et code-behind. À un certain point du développement futur, il pourrait être utile de recomposer cela dans un contrôle personnalisé pour une plus grande possibilité de réutilisation.
  • BoardSpace représente chaque espace sur le plateau de jeu. Il s’agit d’un contrôle personnalisé qui dérive de la classe Button, profitant de la propriété Command, mais en remplacement de l’interface utilisateur par défaut. Pour des remplacements plus simples de l’interface utilisateur, vous pouvez utiliser un Button et simplement définir la propriété Style, comme illustré par les boutons de texte sur la page de démarrage. BoardSpace est un contrôle personnalisé car il utilise une propriété de dépendance personnalisée pour piloter ses transitions animées de l’interface utilisateur. Parce que c’est un contrôle personnalisé, il est défini dans le fichier BoardSpace.cs et le fichier Reversi/Themes/Generic.xaml.
  • PlayerStatus représente le tableau de score et l’horloge. Il s’agit d’un contrôle personnalisé qui dérive de la classe Control et est défini dans le fichier PlayerStatus.cs et le fichier Generic.xaml. C’est un contrôle personnalisé car il intègre des contrôles BoardSpace et exige une plus grande maîtrise de l’initialisation de l’état visuel qu’un UserControl ne peut fournir. Plus précisément, il utilise une substitution de la méthode OnApplyTemplate pour initialiser le BoardSpace à l’un des deux états avant le déclenchement automatique des transitions animées par l’initialisation de la liaison de données. Cette méthode n’est pas disponible sur la classe UserControl, où la chance la plus précoce de modifier le contrôle après la liaison de données réside dans un gestionnaire d’événements Loaded—trop tard pour empêcher les transitions animées par défaut.

Au moment de l’exécution, les contrôles de Reversi ressemblent à ceci.

Page du jeu Reversi montrant les contrôles PlayerStatus, Board et BoardSpace

Menus volants de paramètres

Reversi comprend deux contrôles SettingsFlyout, qui sont dans le dossier View/Settings :

  • DisplaySettings comprend des options permettant d’afficher ou de masquer l’horloge et les indicateurs, qui montrent les déplacements valides et les effets du dernier déplacement.
  • NewGameSettings inclut des options pour modifier la taille du plateau, basculer entre joueur humain et ordinateur, et changer la profondeur de recherche d’IA.

Menus volants de paramètres Reversi

Pour plus d’informations, voir Menus volants de paramètres dans Scénarios de fonctionnalités Reversi.

Données de conception

Le dossier Views/SampleData contient les définitions XAML des instances de GameViewModel et SettingsViewModel. Ces données sont référencées par plusieurs fichiers d’IU XAML dans le dossier Views afin d’afficher une vue significative dans le concepteur. Elles ne sont pas utilisées au moment de l’exécution.

Les données du concepteur SettingsViewModel utilisent également la classe GameDesignerStub dans le dossier Models. Le concepteur ne peut pas facilement utiliser la classe GameFactory pour créer une instance du composant de jeu C# ou C++. La classe GameDesignerStub fournit donc une implémentation allégée de IGame que le concepteur peut instancier à partir des données XAML.

La couche modèle d’affichage

La couche modèle d’affichage réside dans le dossier ViewModels du projet Reversi et inclut le code d’interface utilisateur non visuel et le code qui interagit avec la couche modèle.

Comme mentionné précédemment, les affichages utilisent l’Extension de balisage Binding XAML pour se connecter aux propriétés du modèle d’affichage. Les modèles d’affichage, cependant, n’ont aucune référence directe aux vues. Au lieu de cela, ils fournissent les propriétés et les commandes pour la vue à laquelle se lier.

Les propriétés du modèle d’affichage peuvent fournir :

  • l’exposition directe des propriétés du modèle ;
  • des propriétés du modèle reformatées ou de type converti ;
  • des valeurs calculées à partir de plusieurs propriétés du modèle ou valeurs de retour d’appel de méthode ;
  • des commandes qui convertissent l’entrée de l’utilisateur en appels de méthode du modèle ;
  • des valeurs qui représentent des états de l’interface utilisateur ou de l’application qui ne sont pas pertinentes pour le modèle.

Par exemple, l’interface utilisateur de Reversi doit restituer l’état de chaque espace sur le plateau, qui est stocké dans l’objet modèle Game. Toutefois, l’interface utilisateur doit également afficher des indicateurs sur les déplacements autorisés et les espaces affectés par le mouvement le plus récent. La classe Game inclut ces informations sous la forme de méthodes de validation du mouvement et de valeurs de retour des méthodes move. Toutefois, le modèle d’affichage est nécessaire pour réunir ces informations dans une seule propriété (propriété d’indexation de chaîne this de la classe GameViewModel) à laquelle l’affichage peut se lier.

Reversi comprend 3 modèles d’affichage :

  • GameViewModel contient toutes les interactions avec la classe modèle Game et inclut une référence à un ClockViewModel.
  • SettingsViewModel contient des propriétés qui stockent les paramètres actuels de l’application et inclut une référence au GameViewModel qui représente la partie en cours.
  • ClockViewModel contient toutes les fonctionnalités d’horloge, y compris le formatage de l’affichage et les commandes de pause et de démarrage de l’horloge.

Le dossier ViewModels comprend également :

  • les définitions des interfaces ISettingsViewModel, IGameViewModel et IClockViewModel, qui découplent les tests unitaires du modèle d’affichage des implémentations du modèle d’affichage. Pour plus d’informations, voir la section Tests unitaires ;
  • des énumérations Player et BoardSpaceState, qui fournissent les valeurs de certaines propriétés du modèle d’affichage ;

Pour plus d’informations, voir Liaison de données et Partage de contenu dans Scénarios de fonctionnalités Reversi.

Infrastructure et code commun

Le projet Reversi inclut également du code d’infrastructure pour tout faire fonctionner ensemble et du code réutilisable qui complète les bibliothèques de la plate-forme.

Les fichiers d’infrastructure principaux sont le fichier App.xaml et son code-behind et le fichier Package.appxmanifest. Pour plus d’informations sur ces fichiers, voir Modèles de projet C#, VB et C++ pour les applications du Windows Store et Utilisation du concepteur de manifeste.

Le dossier Common contient des fichiers qui sont adaptés pour la réutilisation dans d’autres projets. La plupart de ces fichiers sont générés par les modèles d’application et d’élément dans Visual Studio. Cependant, Reversi ajoute quelques fichiers et apporte quelques petites modifications aux fichiers générés par un modèle.

Ces fichiers prennent en charge les scénarios de liaison de données courants :

  • BindableBase fournit la classe de base pour chaque modèle d’affichage. Cette classe fournit le code d’infrastructure réutilisable avec notification des modifications, qui fonctionne avec le système de liaison de données. Pour plus d’informations, voir Liaison de données dans Scénarios de fonctionnalité Reversi.
  • DelegateCommand et DelegateCommandBase fournissent des implémentations de ICommand qui vous permettent de définir une fonctionnalité de commande inline dans une implémentation de propriété Bindable. Cette implémentation est copiée à partir de Prism pour Windows Runtime, et est nécessaire, car la classe RelayCommand générée par le modèle ne prend pas en charge les méthodes de commandes asynchrones.
  • NullStateToVisibilityConverter offre un moyen standard pour les liaisons de convertir des valeurs non null en valeurs Visible et des valeurs null en valeurs Collapsed. Pour effectuer cette conversion, au lieu d’avoir à créer des propriétés de modèle d’affichage spéciales, vous pouvez utiliser ce convertisseur.
  • BooleanToVisibilityConverter effectue une conversion semblable entre des valeurs Boolean et Visibility. Ce convertisseur est généré par des modèles Visual Studio, mais Reversi utilise une version modifiée de ce dernier pour accepter une valeur ConverterParameter qui inverse la conversion.
  • ObservableDictionary fournit une classe de modèle d’affichage par défaut pour les pages générées par un modèle.

Ces fichiers prennent en charge la disposition Page et la navigation, y compris la mise en suspens et la reprise :

  • RichTextColumns fournit un panneau qui affiche le contenu de texte enrichi sur une ou plusieurs colonnes selon la taille de l’écran, la résolution et l’état d’affichage. Reversi utilise cette classe dans la HelpPage.
  • SuspensionManager est une classe générée par un modèle pour sauvegarder et restaurer l’état de l’application temporaire lorsque votre application est suspendue ou arrêtée. Reversi utilise une version modifiée de cette classe afin qu’elle puisse sérialiser les modèles d’affichage interconnectés.
  • NavigationHelper est une classe générée par un modèle, qui sert à exécuter des tâches usuelles telles que la navigation vers l’arrière et vers l’avant, ainsi que le chargement ou l’enregistrement des données de la page.
  • RelayCommand fournit une implémentation simple de ICommand, utilisée par NavigationHelper et les boutons Précédent des pages générées par le modèle. Cette classe est similaire à DelegateCommand mais elle ne prend pas en charge les méthodes de commandes asynchrones.

En outre, la classe Toast fournit un service de notification toast simple.

Tests unitaires

Le dossier Tests contient deux projets :

  • le projet ReversiGameComponentTests inclut des tests unitaires pour les projets des composants de jeu Reversi. Ces tests fonctionnent avec les versions C# et C++ du composant de jeu et ils examinent la version configurée pour une utilisation dans le fichier Models\GameFactory.cs du projet Reversi ;
  • le projet ReversiViewModelTests inclut des tests unitaires pour les classes view-model du projet Reversi.

Les tests du projet ReversiGameComponentTests ont été créés durant le développement de la logique de base du jeu pour vérifier diverses hypothèses de conception au fur et à mesure de la croissance de complexité du code. La complexité ayant quelque peu rendu le code sujet aux erreurs, ces tests unitaires ont permis d’identifier et de diagnostiquer les bogues et d’améliorer progressivement la conception du code.

Les tests sont divisés en 4 classes :

  • BasicFunctionalityTests comprend des tests pour les API Board, Moves et Move de la classe Game et des fonctionnalités comme annuler et rétablir.
  • ValidationTests comprend des tests du code qui vérifie si un déplacement particulier est autorisé.
  • AiTests comprend des tests qui vérifient le comportement de l’IA du jeu, qui est en réalité le code qui évalue la qualité des différents déplacements.
  • SerializationTests comprend des tests du code qui représente les mouvements et les états du jeu sous forme de chaînes. Ce code est utile dans les autres tests.

Ces tests ne fournissent qu’une petite couverture, mais ont été suffisants pour améliorer sensiblement le développement.

Les tests du projet ReversiViewModelTests ont été créés pour remplir la couverture des tests unitaires et pour illustrer comment gérer les complexités supplémentaires des tests du modèle d’affichage. Les modèles d’affichage sont plus compliqués à tester car ils ont des dépendances envers des classes de modèle et les uns envers les autres. Pour garantir que les tests unitaires testent uniquement les modèles d’affichage spécifiques, toutes les dépendances externes sont remplies à l’aide d’objets fictifs qui fournissent des implémentations minimales des interfaces requises.

Comme les autres avantages du MVVM, les tests unitaires ne se sont pas absolus. Même en petite quantité, la séparation du code et les tests unitaires peuvent fournir beaucoup de valeur. En fait, en commençant avec de petites quantités vous pourrez mieux comprendre ce qui fonctionne et ce qui ne fonctionne pas. De cette façon, vous pouvez gérer efficacement la complexité croissante d’une application au cours du développement et cela vous évite d’ajouter de la complexité tant que vous n’en avez pas besoin.

Pour plus d’informations, voir Création et exécution de tests unitaires dans une application du Windows Store.

Rubriques associées

Exemple d’application Reversi
Reversi, un jeu du Windows Store en XAML, C# et C++
Utiliser le modèle Model-View-ViewModel (MVVM)
Découvrez comment l’exemple Reversi utilise les fonctionnalités d’application du Windows Store
Comprendre la structure de l’application Reversi
Découvrir le moteur de jeu C++ Reversi
Créer votre première application du Windows Store en C# ou Visual Basic
Feuille de route pour les applications Windows Runtime en C# ou Visual Basic
Liaison de données

 

 

Afficher:
© 2014 Microsoft