Patrons d’architecture Android partie 1 : Modèle-Vue-Contrôleur

Florina Muntenescu
Florina Muntenescu

Follow

Nov 1, 2016 – 6 min lu

Il y a un an, lorsque la majorité de l’équipe Android actuelle a commencé à travailler chez upday, l’application était loin d’être l’application robuste et stable que nous voulions. Nous avons essayé de comprendre pourquoi notre code était en si mauvais état et nous avons trouvé deux principaux coupables : le changement continu de l’interface utilisateur et l’absence d’une architecture supportant la flexibilité dont nous avions besoin. L’application en était déjà à sa quatrième refonte en six mois. Le patron de conception choisi semblait être Modèle-Vue-Contrôleur mais était alors déjà un « mutant », loin de ce qu’il devrait être.

Découvrons ensemble ce qu’est le patron Modèle-Vue-Contrôleur ; comment il a été appliqué dans Android au fil des années ; comment il devrait être appliqué afin de maximiser la testabilité ; et certains de ses avantages et inconvénients.

Dans un monde où la logique de l’interface utilisateur a tendance à changer plus souvent que la logique métier, les développeurs de bureau et Web avaient besoin d’un moyen de séparer les fonctionnalités de l’interface utilisateur. Le modèle MVC était leur solution.

  • Modèle – la couche de données, responsable de la gestion de la logique métier et du traitement de l’API du réseau ou de la base de données.
  • Vue – la couche d’interface utilisateur – une visualisation des données du modèle.
  • Contrôleur – la couche logique, reçoit une notification du comportement de l’utilisateur et met à jour le modèle si nécessaire.

Structure de classe Modèle-Vue-Contrôleur

Donc, cela signifie que le contrôleur et la vue dépendent tous deux du Modèle : le contrôleur pour mettre à jour les données, la vue pour obtenir les données. Mais, le plus important pour les développeurs Web et de bureau de l’époque : le modèle était séparé et pouvait être testé indépendamment de l’interface utilisateur. Plusieurs variantes de MVC sont apparues. Les plus connues sont liées au fait que le Modèle est passif ou notifie activement qu’il a été modifié. Voici plus de détails:

Modèle passif

Dans la version Modèle passif, le contrôleur est la seule classe qui manipule le Modèle. En fonction des actions de l’utilisateur, le Contrôleur doit modifier le Modèle. Après que le Modèle ait été mis à jour, le Contrôleur notifiera à la Vue qu’elle doit également se mettre à jour. À ce moment-là, la Vue demandera les données du Modèle.

Modèle-Vue-Contrôleur – modèle passif – comportement

Modèle actif

Pour les cas où le contrôleur n’est pas la seule classe qui modifie le modèle, le modèle a besoin d’un moyen de notifier la vue, et les autres classes, des mises à jour. Ceci est réalisé avec l’aide du pattern Observer. Le Modèle contient une collection d’observateurs qui sont intéressés par les mises à jour. La Vue implémente l’interface d’observateur et s’enregistre en tant qu’observateur du Modèle.

Modèle-Vue-Contrôleur – Modèle actif – structure de classe

Chaque fois que le Modèle se met à jour, il va également itérer à travers la collection d’observateurs et appeler la méthode update. L’implémentation de cette méthode dans la Vue déclenchera alors la requête des dernières données du Modèle.

Modèle-Vue-Contrôleur – Modèle actif – comportement

Modèle-Vue-Contrôleur dans Android

Vers 2011, quand Android a commencé à devenir de plus en plus populaire, des questions d’architecture sont naturellement apparues. Comme MVC était l’un des patterns d’interface utilisateur les plus populaires à l’époque, les développeurs ont essayé de l’appliquer à Android également.

Si vous recherchez sur StackOverflow des questions comme « Comment appliquer MVC dans Android », l’une des réponses les plus populaires a déclaré que dans Android, une activité est à la fois la vue et le contrôleur. Avec le recul, cela semble presque fou ! Mais, à ce moment-là, l’accent principal était mis sur le fait de rendre le modèle testable et généralement le choix d’implémentation pour la vue et le contrôleur dépendait de la plateforme.

Comment appliquer le MVC dans Android

De nos jours, la question de savoir comment appliquer les patterns MVC a une réponse plus facile à trouver. Les activités, les fragments et les vues devraient être les vues dans le monde MVC. Les contrôleurs devraient être des classes séparées qui n’étendent ou n’utilisent aucune classe Android, et même chose pour les modèles.

Un problème se pose lors de la connexion du contrôleur à la vue, puisque le contrôleur doit dire à la vue de se mettre à jour. Dans l’architecture passive Model MVC, le contrôleur doit détenir une référence à la vue. La façon la plus simple de le faire, tout en se concentrant sur les tests, est d’avoir une interface BaseView, que l’Activité/Fragment/Vue étendrait. Ainsi, le contrôleur aurait une référence à la BaseView.

Avantages

Le pattern Modèle-Vue-Contrôleur supporte fortement la séparation des préoccupations. Cet avantage augmente non seulement la testabilité du code, mais il le rend également plus facile à étendre, permettant une implémentation assez facile de nouvelles fonctionnalités.

Les classes Model n’ont pas de référence aux classes Android et sont donc simples à tester à l’unité. Le contrôleur n’étend ou n’implémente aucune classe Android et doit avoir une référence à une classe d’interface de la vue. De cette façon, les tests unitaires du contrôleur sont également possibles.

Si les vues respectent le principe de responsabilité unique, alors leur rôle est juste de mettre à jour le contrôleur pour chaque événement utilisateur et juste d’afficher les données du modèle, sans implémenter aucune logique métier. Dans ce cas, les tests UI devraient être suffisants pour couvrir les fonctionnalités de la vue.

La vue dépend du contrôleur et du modèle

La dépendance de la vue vis-à-vis du modèle commence à être un inconvénient dans les vues complexes. Afin de minimiser la logique dans la vue, le modèle devrait être capable de fournir des méthodes testables pour chaque élément qui doit être affiché. Dans une implémentation active du Modèle, cela augmente exponentiellement le nombre de classes et de méthodes, étant donné que des Observateurs pour chaque type de données seraient nécessaires.

Du fait que la Vue dépend à la fois du Contrôleur et du Modèle, les changements dans la logique de l’IU pourraient nécessiter des mises à jour dans plusieurs classes, diminuant la flexibilité du pattern.

Qui gère la logique UI ?

Selon le pattern MVC, le contrôleur met à jour le modèle et la vue obtient les données à afficher à partir du modèle. Mais qui décide de la manière d’afficher les données ? Est-ce le Modèle ou la Vue ? Prenons l’exemple suivant : nous avons un User, avec un prénom et un nom de famille. Dans la vue, nous devons afficher le nom de l’utilisateur comme « Nom, Prénom » (par exemple, « Doe, John »).

Si le rôle du Modèle est juste de fournir les données « brutes », cela signifie que le code dans la vue serait:

String firstName = userModel.getFirstName(); 
String lastName = userModel.getLastName(); nameTextView.setText(lastName + ", " + firstName)

Cela signifie donc que ce serait la responsabilité de la vue de gérer la logique UI. Mais cela rend la logique de l’interface utilisateur impossible à tester à l’unité.

L’autre approche consiste à faire en sorte que le modèle expose uniquement les données qui doivent être affichées, en cachant toute logique métier à la vue. Mais alors, nous nous retrouvons avec des modèles qui gèrent à la fois la logique métier et la logique d’interface utilisateur. Ce serait testable à l’unité, mais alors le Modèle finit, implicitement par être dépendant de la Vue.

String name = userModel.getDisplayName(); nameTextView.setText(name);

Conclusion

Dans les premiers jours d’Android, le pattern Modèle-Vue-Contrôleur semble avoir dérouté beaucoup de développeurs et conduit à un code difficile, voire impossible à tester à l’unité.

La dépendance de la vue par rapport au modèle et le fait d’avoir une logique dans la vue ont orienté notre code-base vers un état dont il était impossible de se remettre sans refactorer complètement l’application. Quelle était la nouvelle approche en matière d’architecture et pourquoi ? Découvrez-le en lisant cet article de blog.