Comment choisir l'architecture iOS appropriée (Partie 2)

MVC, MVP, MVVM, VIPER ou VIP

Vous pouvez consulter la première partie ici.

Les principales architectures iOS

Un bref aperçu.

MVC

Les couches MVC sont les suivantes:

M: Logique d'entreprise, couche réseau et couche d'accès aux données

V: Couche UI (choses UIKit, Storyboards, Xibs)

C: coordonne la médiation entre le modèle et la vue.

Pour comprendre MVC, nous devons comprendre le contexte dans lequel il a été inventé. MVC a été inventé dans les anciens jours de développement Web, où Views n'a pas d'état. Dans l'ancien temps, chaque fois que nous avions besoin d'un changement visuel dans le site Web, le navigateur rechargeait à nouveau tout le code HTML. À l'époque, aucun concept de l'état de la vue n'était maintenu et enregistré.

Il y avait, par exemple, certains développeurs qui se mélangeaient dans le même fichier HTML, PHP et accès à la base de données. La principale motivation de MVC était donc de séparer la couche View de la couche Model. Cela a augmenté la testabilité de la couche modèle. Soi-disant dans MVC, les couches View et Model ne devraient rien savoir l'une de l'autre. Pour que cela soit possible, une couche intermédiaire nommée Controller a été inventée. C'est le PÉR qui a été appliqué.

Un exemple du cycle MVC:

  1. Une action / un événement utilisateur dans la couche d'affichage (par exemple: Action d'actualisation) est déclenché et cette action est communiquée au contrôleur.
  2. Le contrôleur qui demande des données à la couche modèle
  3. Modélisez les données de retour au contrôleur
  4. Le contrôleur dit que pour la vue, mettez à jour son état avec les nouvelles données
  5. Voir mettre à jour son état

Apple MVC

Dans iOS, le View Controller est couplé à l'UIKit et à la vue du cycle de vie, il ne s'agit donc pas de MVC pur. Cependant, dans la définition MVC, rien ne dit que le contrôleur ne peut pas connaître l'implémentation spécifique à la vue ou au modèle. Son objectif principal est de séparer les responsabilités de la couche modèle de la couche vue afin que nous puissions la réutiliser et tester la couche modèle isolément.

Le ViewController contient la vue et possède le modèle. Le problème est que nous avons utilisé pour écrire le code du contrôleur ainsi que le code de vue dans le ViewController.

MVC crée souvent le problème appelé Massive View Controller, mais cela ne se produit que et devient une chose sérieuse dans les applications avec suffisamment de complexité.

Le développeur peut utiliser certaines méthodes pour rendre le contrôleur de vue plus facile à gérer. Quelques exemples:

  • Extraction de la logique VC pour d'autres classes comme la source de données des méthodes d'affichage de table et délégation pour d'autres fichiers à l'aide du modèle de conception délégué.
  • Créez une séparation plus distincte des responsabilités avec la composition (par exemple, divisez le VC en contrôleurs de vue enfant).
  • Utilisez le modèle de conception du coordinateur pour supprimer la responsabilité d'implémenter la logique de navigation dans le VC
  • Utilisez une classe wrapper DataPresenter qui encapsule la logique et transforme le modèle de données en une sortie de données représentant les données présentées à l'utilisateur final.

MVC vs MVP

Comment vous pouvez voir le diagramme de MVP est très similaire à MVC

Le MVC était un pas en avant, mais il était encore marqué par l'absence ou le silence sur certaines choses.

Pendant ce temps, le World Wide Web s'est développé et beaucoup de choses dans la communauté des développeurs ont évolué. Par exemple, les programmeurs ont commencé à utiliser Ajax et ne chargent que des parties de pages au lieu de la page HTML entière à la fois.

Dans MVC, je pense que rien n'indique que le contrôleur ne devrait pas connaître l'implémentation spécifique de View (absence).

HTML faisait partie de la couche View et de nombreux cas étaient stupides. Dans certains cas, il ne reçoit que les événements de l'utilisateur et affiche le contenu visuel de l'interface graphique.

Alors que des parties des pages Web commençaient à être chargées en plusieurs parties, cette segmentation a conduit à maintenir l'état d'affichage et un besoin accru d'une séparation des responsabilités de la logique de présentation.

La logique de présentation est la logique qui contrôle la façon dont l'interface utilisateur doit être affichée et comment les éléments d'interface utilisateur interagissent ensemble. Un exemple est la logique de contrôle de quand un indicateur de chargement devrait commencer à montrer / animer et quand il devrait cesser de montrer / animer.

Dans MVP et MVVM, la couche d'affichage devrait être muette comme une baise sans logique ni intelligence, et dans iOS, le contrôleur de vue devrait faire partie de la couche d'affichage. Le fait que View soit stupide signifie que même la logique de présentation reste en dehors de la couche View.

L'un des problèmes de MVC est qu'il n'est pas clair où la logique de présentation doit rester. Il est simplement silencieux à ce sujet. La logique de présentation doit-elle se trouver dans la couche Vue ou dans la couche Modèle?

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

Prenons l'exemple suivant: nous avons un utilisateur, avec prénom et nom. Dans la vue, nous devons afficher le nom d'utilisateur comme «Nom, prénom» (par exemple, «Flores, Tiago»).

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

laissez firstName = userModel.getFirstName ()
laissez lastName = userModel.getLastName ()
nameLabel.text = lastName + "," + firstName

Cela signifie donc qu'il serait de la responsabilité de View de gérer la logique de l'interface utilisateur. Mais cela rend la logique de l'interface utilisateur impossible à tester unitaire.

L'autre approche consiste à demander au modèle d'exposer uniquement les données qui doivent être affichées, en masquant toute logique métier de la vue. Mais ensuite, nous nous retrouvons avec des modèles qui gèrent à la fois la logique métier et la logique de l'interface utilisateur. Il serait testable à l'unité, mais le modèle finit par être implicitement dépendant de la vue.

let name = userModel.getDisplayName ()
nameLabel.text = nom

Le MVP est clair à ce sujet et la logique de présentation reste dans la couche Presenter. Cela augmente la testabilité de la couche Presenter. Maintenant, le calque de modèle et de présentateur est facilement testable.

Normalement, dans les implémentations MVP, la vue est cachée derrière une interface / un protocole et il ne doit y avoir aucune référence à l'UIKit dans le présentateur.

Une autre chose à garder à l'esprit est les dépendances transitives.

Si le contrôleur a une couche métier comme dépendance et que la couche entreprise a une couche d'accès aux données comme dépendance, alors le contrôleur a une dépendance transitive pour la couche d'accès aux données. Étant donné que les implémentations MVP utilisent normalement un contrat (protocole) entre toutes les couches, elles n'ont pas de dépendances transitives.

Les différentes couches changent également pour différentes raisons et à des rythmes différents. Ainsi, lorsque vous modifiez un calque, vous ne voulez pas que cela provoque des effets / problèmes secondaires dans les autres calques.

Les protocoles sont plus stables que les classes. Les protocoles n'ont pas de détails d'implémentation et avec les contrats, il est donc possible de modifier les détails d'implémentation d'une couche sans affecter les autres couches.

Les contrats (protocoles) créent donc un découplage entre les couches.

MVP vs MVVM

Diagramme MVVM

L'une des principales différences entre MVP et MVVM est que dans MVP, le présentateur communique avec la vue via des interfaces, et dans MVVM, la vue est orientée vers les modifications de données et d'événements.

Dans The MVP, nous établissons une liaison manuelle entre Presenter et View à l'aide d'interfaces / protocoles.
Dans The MVVM, nous effectuons une liaison de données automatique en utilisant quelque chose comme RxSwift, KVO ou utilisons un mécanisme avec des génériques et des fermetures.

Dans le MVVM, nous n'avons même pas besoin d'un contrat (par exemple: interface java / protocole iOS) entre ViewModel et View car nous communiquons généralement via le modèle de conception Observer.

Le MVP utilise le modèle de délégué parce que le délégué présente des commandes à la couche de vue, il doit donc connaître quelque chose sur la vue même s'il ne s'agit que de la signature d'interface / protocole. Pensez à la différence entre Notification Center et TableView Delegates. Le centre de notification n'a pas besoin d'interfaces pour créer un canal de communication, mais les délégués TableView utilisent un protocole que les classes doivent implémenter.

Pensez à la logique de présentation d'un indicateur de chargement. Dans MVP, le présentateur fait ViewProtocol.showLoadingIndicator. Dans MVVM, il peut y avoir une propriété isLoading dans le ViewModel. La couche View via une liaison de données automatique détecte quand cette propriété change et s'actualise. MVP est plus impératif que MVVM car le présentateur donne des ordres.

MVVM concerne davantage les modifications de données que les commandes directes, et nous faisons des associations entre les modifications de données et les mises à jour des vues. Si nous utilisons RxSwift et le paradigme de programmation réactive fonctionnelle avec MVVM, nous avons rendu le code encore moins impératif et plus déclaratif.

MVVM est plus facile à tester que MVP car MVVM utilise le modèle de conception Observer qui transfère les données entre les composants de manière découplée.
Nous pouvons donc tester simplement en regardant les changements dans les données simplement en comparant les deux objets plutôt qu'en créant des simulations des appels de méthodes pour tester la communication entre View et Presenter.

PS: J'ai fait quelques mises à jour de l'article qui l'ont fait grandir, il a donc fallu le diviser en trois parties. Vous pouvez lire la troisième partie ici.

La deuxième partie se termine ici. Tous les commentaires sont les bienvenus. La troisième partie parlera de VIPER, VIP, programmation réactive, compromis, contraintes et sens contextuel.

Merci pour la lecture! Si vous avez aimé cet article, veuillez applaudir
pour que les autres puissent aussi le lire :)