Pattern MVC et propreté du code

TerflogagTerflogag Membre
octobre 2015 modifié dans API UIKit #1

Bonjour,


 


Voila maintenant quelques temps je programme sans me soucier réellement de la propreté du code, je souhaiterais changer cela dans une optique de progression et de qualité.


 


J'ai donc par exemple une application assez simple : une image s'affiche après un swipe sur l'écran.


A chaque swipe, je récupère un objet contenant une image qui sera affiché une fois télécharger.


Je dispose d'une classe représentant mon object, qui contient les traitements dont j'ai besoin a l'initialisation. En revanche je fais le téléchargement de l'image depuis mon controller.


 


Est ce correct ? Ou dois-je forcement télécharger l'image depuis mon object, puis notifier mon controller afin qu'il récupère l'image et l'affiche ? 


 


La seconde solution me paraà®t plus "propre" dans le cadre du pattern MVC, mais bien plus "compliquer" aussi. (notification center, récupération de l'image, m'assurer qu'il s'agisse de la bonne notification)


 


Bonne soirée


Réponses

  • oui, c'est mieux de faire le téléchargement depuis l'objet. Tu n'as pas besoin de notifications en revanche, et ce serait mal faire (utiliser le mauvais pattern) que de les utiliser. Un simple completionBlock ici sera bien mieux.
  • CéroceCéroce Membre, Modérateur

    Si tu veux, l'idée est de mettre le moins de code possible dans le contrôleur pour les raisons suivantes:


    - il devient vite gros


    - le code du contrôleur a tendance à  se répéter d'un contrôleur à  un autre, alors qu'on agit sur le même objet du modèle


    - les contrôleurs sont très difficile à  tester en test unitaire.


     


    Donc, en général, on va préférer mettre le maximum de code dans le modèle ("Fat models, skinny controllers").


    Il ne faut pas oublier que le rôle d'un contrôleur est de faire le lien entre le modèle et les vues, et pas plus, même si la conception de UIViewController tend à  rendre cela difficile.


    La blague actuelle est que chez Apple, MVC veut dire "Massive View Controller".


  • FKDEVFKDEV Membre
    octobre 2015 modifié #4

    Le minimum de la qualité c'est de bien nommer les choses : les fonctionnalités; les concepts, les classes et les variables.


    Par "bien", j'entends avec des noms assez précis et adapté au contexte (dont les conventions). Autrement dit des noms qui auront encore un sens pour toi dans six mois.


     


    A la lumière de ce principe, ton explication est trop abstraite. On ne sait pas trop ce que fait ton programme, donc il est difficile de donner un avis.


    Par exemple, il est difficile de savoir où classer ce que tu appelles  "mon objet", celui qui contient une image.


    Est-ce une partie du modèle (les données), un objet qui gère une ressource, une vue, autre chose...




  • Si tu veux, l'idée est de mettre le moins de code possible dans le contrôleur pour les raisons suivantes:


    - il devient vite gros


    - le code du contrôleur a tendance à  se répéter d'un contrôleur à  un autre, alors qu'on agit sur le même objet du modèle


    - les contrôleurs sont très difficile à  tester en test unitaire.


     


    Donc, en général, on va préférer mettre le maximum de code dans le modèle ("Fat models, skinny controllers").


    Il ne faut pas oublier que le rôle d'un contrôleur est de faire le lien entre le modèle et les vues, et pas plus, même si la conception de UIViewController tend à  rendre cela difficile.


    La blague actuelle est que chez Apple, MVC veut dire "Massive View Controller".




    Oui je trouve que tu as bien raison !


    Je me demandais si ça venait de ma façon de coder mais rien à  faire, mes controllers contiennent toujours pas mal de codes. Ce que je constate en général c'est une vue (du point de vue utilisateur) correspond à  un controller.


    Bref les controllers sont pas simples à  factoriser :/

  • TerflogagTerflogag Membre
    octobre 2015 modifié #6

    Merci pour vos réponse, cela me conforte un peu dans mon idées d'évoluer la façon dont sont conçus mes applications.


     




    Le minimum de la qualité c'est de bien nommer les choses : les fonctionnalités; les concepts, les classes et les variables.


    Par "bien", j'entends avec des noms assez précis et adapté au contexte (dont les conventions). Autrement dit des noms qui auront encore un sens pour toi dans six mois.


     


    A la lumière de ce principe, ton explication est trop abstraite. On ne sait pas trop ce que fait ton programme, donc il est difficile de donner un avis.


    Par exemple, il est difficile de savoir où classer ce que tu appelles  "mon objet", celui qui contient une image.


    Est-ce une partie du modèle (les données), un objet qui gère une ressource, une vue, autre chose...




     


    Alors dans mon cas, deux classes :  


    - la premiere étant l'object contenant mon image (le Model) 


    - la seconde est mon Controller (ce qui gère l'affichage) 


     




    oui, c'est mieux de faire le téléchargement depuis l'objet. Tu n'as pas besoin de notifications en revanche, et ce serait mal faire (utiliser le mauvais pattern) que de les utiliser. Un simple completionBlock ici sera bien mieux.




     


    Je dispose déjà  d'un completionBlock dans mon controller, mais du coup en déplacement tout ca dans mon Model, je vois difficilement comment mettre à  jour sans notifications. Si mon Controller n'est plus actif lorsque le block se termine cela pose un soucis.. c'est surement tout bête mais j'ai du mal a me visualiser comment faire cela !


  • FKDEVFKDEV Membre
    octobre 2015 modifié #7
    Peut-être que le completionBlock doit être dans le controller.
    Le controller doit être le lieu de rencontre des autres objets, la glue.
    Par exemple:
     
    -(void)didSwipe:(UIGestureRecognizer*)gestureRecognizer
    {
    if (myModel.image)
    {
    myImageView.image = myModel.image
    }
    else
    {
    [self showActivityIndicator];
    [[Downloader sharedInstance] getImageFromURL:myModel.imageURL completionBlock:^(UIImage* image)
    {
    if (image)
    {
    myModel.image = image;

    myImageView.image = myModel.image;
    }
    [weakSelf hideActivityIndicator];
    }];
    }

    }
    Bon le contenu de cet exemple peut se discuter, mais on voit bien un mélange de trois types d'objets : les vues, les modèles et les fournisseurs de services.

    Si la rencontre entre les trois se fait dans le Controller, alors les trois peuvent rester "propres".

  •  


    Si j'avais eu un cas comme ça à  traiter pour de vrai, je n'aurais peut-être pas mis l'image dans le model mais dans un objet intermédiaire. Une sorte de façade dynamique permettant d'enrichir le modèle au runtime.



     


    Tu peux expliquer cette dernière ligne, que veux tu dire par "enrichir le modèle au runtime" ?


    Merci ;)


  • FKDEVFKDEV Membre
    octobre 2015 modifié #9
    Argh. Trop rapide. J'avais supprimé cette ligne de mon post.

    Ben en fait ce que je veux dire, c'est qu'on a intérêt à  avoir un modèle le plus simple possible.
    En gros des données et quelques règles métiers.

    Après, pour l'affichage du modèle, tu as besoin de l'enrichir avec des images ou des textes qui peuvent dépendre de la langue ou du type de vue.


    Par exemple si tu dois afficher un statut de stock (vide, faible ou complet).
    Dans le modèle, tu vas avoir un enum avec trois valeurs possibles.
    Mais à  l'affichage, tu vas vouloir afficher un petit icône dans les vues (disons une pastille rouge, orange et verte).

    Tu as donc une correspondance à  faire entre tes valeurs d'enum (vide, faible, complet) et tes couleurs.
    Concrètement, tu auras un petit switch/case.

    Je vois quatre endroits (il y en surement d'autres) pour mettre ce switch/case/

    A/ Tu le mets dans le modèle. Cela t'oblige à  ajouter des fonctions orientées affichage dans le modèle (à  base d'UIImage ou d'UIColor, complètement dépendantes de la plateforme et redondantes avec les données pures).
    B/ Tu mets le switch case dans le controller. Et là , tu vas te retrouver avec un controller massif et des duplications de codes dans les différents controllers (car tu as peut-être plusieurs controllers qui veulent afficher ces pastilles).
    C/ Tu crées un objet entre le controller et le modèle dont le but est d'adapter le model à  l'affichage. Cela peut aussi être une extension/catégorie du model si le langage le permet.
    D/ Tu crées une vue spécialisée dont le rôle est d'afficher une pastille et tu lui passes un pointeur vers l'objet du modèle.


    Dans le cadre d'un petit projet les choix A et B sont acceptables. Tu peux commencer à  passer aux choix C et D au moment où tu te rends compte que tu es en train de dupliquer du code ou quand ton controller devient illisible.
  • ok, c'est intéressant merci !


    Le D/ correspond au V du MVC un peu nan ?


    Corrige moi si je me trompe : j'ai un controller qui charge des vues qui pointent vers mon modèle (on est toujours dans la D ?).


    Par exemple dans ton exemple on pourrait avoir un MyTableViewController qui charge des MyTableViewCell qui vont piocher dans une classe MyModel ?


  • Oui, je ne dis pas que c'est toujours la meilleure solution, mais moi je l'utilise souvent. Notamment quand tu as plusieurs instances de la meme tableview dans ton app, et encore plus si tu as plusieurs instances dans le même écran.


    En fait, d'après les puristes, la D viole un peu le MVC à  la mode Apple où toute interaction entre vue et model passe par le controller.
  • CéroceCéroce Membre, Modérateur

    Moi aussi je le fais souvent, notamment, je sous-classe UITableViewCell qui devient en quelque sorte un contrôleur.


    Il y a un moment où il faut être pragmatique, et tant de code dans le View Controller n'est plus maintenable.




  • Moi aussi je le fais souvent, notamment, je sous-classe UITableViewCell qui devient en quelque sorte un contrôleur.


    Il y a un moment où il faut être pragmatique, et tant de code dans le View Controller n'est plus maintenable.




    ah cool, je fais exactement ça, je me demandais si c'était une bonne pratique. Mais sans ça les controllers deviennent vraiment illisibles.

Connectez-vous ou Inscrivez-vous pour répondre.