Questions sur le pattern MVC

KyoKyo Membre
06:42 modifié dans API UIKit #1
Salut ici,

J'ai commencé depuis début avril à  m'amuser un peu avec xcode et cocoaTouch sur divers tutoriaux. Et donc là , j'arrive à  un moment où je me pose pas mal de questions pour pas prendre de mauvaises habitudes.

Ma question principale concerne le patron de conception MVC. J'ai plus ou moins compris le principe de ce que j'ai lu à  droite, à  gauche mais quand il faut l'implémenter il y a de l'hésitation.

Donc, tout d'abord je vais vous décrire l'exercice sur lequel j'ai essayé d'appliquer ce modèle. C'est l'exercice des cours de Stanford pour celles et ceux qui les suivent.


C'est une application de dessin de polygone à  n côtés. On a deux boutons qui permettent d'augmenter ou de diminuer le nombre de côtés. Un label est présent afin d'indiquer ce nombre.


Alors par rapport au modèle MVC, j'avais pensé à  :

Modèle : Une classe PolygonModel qui contient une ivar correspondant au nombre de côtés ainsi que deux méthodes permettant l'incrémentation et la décrémentation.

Vue : Deux classes PolygonView et DrawingZoneView. Ces deux classes dérivent d'une classe abstraite AbstractPolygonView qui elle même dérive de UIView. Cette classe abstraite permet d'avoir accès à  une instance du contrôleur.
La première classe correspond à  la vue d'ensemble contenant le label du nombre de côté et un délégué de la seconde vue qui elle s'occupe de dessiner le polygon. La vue d'ensemble contient deux IBAction qui vont par délégation par le biais du contrôleur influer sur le nombre de côté dans le modèle.

Contrôleur : Une classe PolygonViewController qui s'occupe de synchroniser la vue et le modèle donc. Cette classe possède une instance du modèle en délégation.


Donc voilà  un rapide descriptif de ce que j'ai fais. Je voulais avoir vos avis sur ce que j'ai implémenté. Comme le petit cours que j'ai lu sur le modèle MVC le préconisait ça rajoute du code en plus alors que ça pourrait être plus simple, donc je pensais être sur la bonne voie vu qu'effectivement, j'ai une impression de redondance de code (par délégation).

Je ouvert à  tout autre remarque, n'étant pas accoutumé à  la gestion de la mémoire (Java inside  :)), je viens vos conseils. Sinon J'ai fais mes initiations dans initWithNibName et dans awakeFromNib. C'est ce que j'avais vu dans les tutoriaux que j'ai fais mais est-ce c'est bien là  qu'il faut les faire ?

J'ai joins en zip le projet xcode si vous vouliez voir plus rapidement ce dont je parle. J'aurais bien un diagramme de classes mais j'ai pas encore eu le temps de trouver ce type de logiciel sur mac.

Vla !  :P





Réponses

  • AliGatorAliGator Membre, Modérateur
    avril 2009 modifié #2
    Hello !

    Je pense que dans l'ensemble tu as de bonnes bases, mais tu te prends trop la tête pour la partie vue.
    Perso moi je me serais contenté d'une seule classe "Vue" pour ce type d'appli : la DrawingZoneView. Le reste, c'est ton contrôlleur qui le gère pour la plupart.

    Le contrôlleur va avoir accès au modèle d'un côté bien sûr, et va avoir accès côté vue :
    (1) à  la DrawingZoneView, pour lui demander de se raffraà®chir pour dessiner un nouveau polygone à  N côtés,
    (2) au UILabel qui va servir à  afficher le nombre de côtés courant (pas besoin d'avoir une sous-classe pour ça, UILabel suffit très bien à  tes besoins)
    et enfin
    (3) ton contrôleur va recevoir de chacun de tes boutons que tu as côté vue (les 2 boutons pour incrémenter/décrémenter le nombre de polygones) une IBAction (une pour chacun). Quand il reçoit cette IBAction, il demande au modèle d'incrémenter ou décrémenter le nombre de côtés, puis il demande le nouveau nombre de côtés, met à  jour le UILabel avec cette nouvelle valeur, et demande au DrawingZoneView de se redessiner avec le nouveau nombre de côtés.


    Je ne pense pas qu'il y ait besoin de faire plus compliqué.


    ----


    Une autre façon de voir les choses possibles serait sinon celle-ci :
    (1) Ton modèle contient le nombre de côtés courant et une méthode pour le modifier (lui affecter une nouvelle valeur quelconque), une autre pour récupérer cette valeur (des accesseurs à  ta variable d'instance quoi, getter+setter)

    (2) Côté vue, une DrawingZoneView, et si tu y tiens, un "control personnalisé" que tu réaliserais dans le but de le rendre réutilisable, consistant en une vue contenant un UILabel et 2 boutons. Ce contrôle personnalisable, qu'on pourrait appeller "IndexChooser" serait lui-même la cible (target) des actions de chacun des boutons, et en réponse à  ces actions incrémenterait ou décrémenterait un compte interne représentant la valeur courante et l'afficherait... et possèderait alors des méthodes exposées à  l'extérieur pour récupérer ou modifier l'index représenté par ce "contrôle personnalisé... et si tu fais dériver cet objet personnalisé de UIContrôl, tu pourras utiliser les mécanismes de target/action pour envoyer une action au target choisi lorsque la valeur (l'index) représenté change, pratique pour relier cette action depuis IB.


    Du coup ta vue serait composée d'une zone pour dessiner le polygone, et de ce contrôle personnalisé (qui doit fonctionner de façon indépendante pour pouvoir être réutilisable dans un projet qui n'a rien à  voir). Quand ton contrôle personnalisé IndexChooser modifie sa valeur, il envoie une action à  ton contrôlleur, qui récupère la nouvelle valeur, la modifie dans le modèle, et demande à  la DrawingZone de se redessiner avec ce nouveau nombre de côtés.



    Et quand tu y penses en regardant de plus près cette solution... oh mais dis donc, ton IndexChooser, il ressemble beaucoup à  des contrôles qui existent déjà , comme un "slider" par exemple ! Et c'est vrai que pourquoi pas utiliser directement un slider du coup ? Oh eh bien justement grace à  la magie du MVC -- et c'est là  que tu en voies l'intérêt -- du coup il est facile de remplacer ton contrôle "IndexChooser" personnalisé par un UISlider fourni par Cocoa, sans trop d'impact sur le reste de ton code !!

    Bon après entre nous, pour ton appli d'exemple, un modèle n'est vraiment pas utile pour le coup : le contrôlleur peut lui-même mémoriser le nombre de côtés courant de ton polygone, c'est un peu bête de créer un modèle juste pour ça... De même pour le "contrôle personnalisé" permettant de choisir le nombre de côtés, un bête "slider" suffit pour le coup.
    Parce que bon c'est une très bonne idée de vouloir prendre les bonnes habitudes dès le début et c'est tout à  ton honneur (et même très prometteur si tu t'y mets dès le début, bravo pour l'initiative)... maintenant dans ce cas précis, c'est p'tet un peu bcp que de créer un modèle pour juste une variable d'instance  :)
  • schlumschlum Membre
    06:42 modifié #3
    Je pense pareil qu'Ali... Une seule classe vue avec des méthodes spécialisées pour dessiner différentes choses.
  • KyoKyo Membre
    06:42 modifié #4
    Oui je suis tout à  fait d'accord, l'exemple n'est pas le plus approprié :) Mais comme je bossais sur les exo de Stanford et le modèle MVC, je me suis dit "Tiens voilà  une bonne occassion de d'appliquer le pattern MVC sur un exo pas trop compliqué".

    Pour ce qui est des vues, en fait Stanford fournissait un bout de code (méthode de classes) qui donnait les coordonnées des points à  dessiner. Comme les points étaient calculés en rapport avec la frame. J'ai pensé à  faire une view de dessin comme ça le polygon n'aurait été dessiner dans la frame de cette dernière.
    Après je suppose qu'on peut faire tout ça dynamiquement sans passer par une autre classe vue mais j'avoue que je ne maà®trise pas encore assez l'environnement de développement pour le faire :P. Va falloir que je m'y mette car sinon je vais me retrouver avec 30 000 classes pour mon projet  :o

    Ca commence à  être plus clair pour moi mais petite question. Les fonctions de type IBAction sont gérées par quelle couche ? Vue, contrôleur ou pas de règle ? Moi j'aurais tendance à  les mettre dans la vue mais je vois des codes où c'est dans le contrôleur.
  • schlumschlum Membre
    06:42 modifié #5
    IBOutlet, IBAction -> Contrôleur
  • KyoKyo Membre
    06:42 modifié #6
    Clair, net et précis. Merci à  vous deux
  • AliGatorAliGator Membre, Modérateur
    06:42 modifié #7
    Pour le cas de ta vue représentant un polygone, je trouve que c'est une bonne idée (d'autant que ton appli est justement spécialisée dans le dessin d'un polygone) de faire une sous-classe de UIView, disons PolygonView, qui se charge du dessin, utilisant sa propre frame pour calculer les points justement, et en fonction du nombre de côtés demandés. Faut pas hésiter pour ce genre de cas en effet à  faire une sous-classe de UIView, c'est même plutôt courant comme pratique.

    Pour les IBActions, ce sont des actions envoyées au contrôleur depuis la vue. Donc c'est la vue qui envoie les actions (qui appelle les méthodes définies comme IBAction) sur leur "target", qui sera donc typiquement bien souvent ton contrôleur. Et le contrôleur doit donc répondre à  ces méthodes / IBActions.

    Donc normalement, dans une architecture classique MVC, ce sont tes contrôles (UIControls, genre tes boutons de l'interface, etc de la partie Vue) qui envoient les actions, et donc dans le code de ta partie "Controlleur" que tu vas implémenter le code de ces IBActions.


    Maintenant il y a des cas où cela peut ne pas être le contrôleur qui reçoit une IBAction, je pense en particulier au cas que j'évoquais tout à  l'heure si tu veux faire un UIControl personnalisé (par exemple une UIView qui contient 2 boutons "<<" et ">>" et un label affichant un entier). Mais en général quand on fait ça... c'est juste pour faire de la "redirection", pour faire ne sorte qu'au final le code de notre IBAction de notre UIControl perso va... appeller une autre IBAction, cette fois ci typiquement dirigée vers le contrôleur... Donc on retombe sur nos pieds. Ou quand on veut "convertir" le mécanisme target/action en un autre mécanisme (appel de méthodes de delegate par exemple), on dirige alors les IBActions vers notre propre classe, et leur code a pour effet d'appeler des méthodes de delegate... ce genre d'astuce... Mais vu qu'au final du coup typiquement le delegate c'est notre Contrôleur... là  encore on retombe sur nos pieds.

    Au final ce qu'il faut retenir c'est que toute action effectuée dans la vue informe le contrôleur de cette action. Un bouton a été cliqué par l'utilisateur ? C'est vers le contrôleur qu'on dirige l'IBAction de ce bouton pour qu'il soit informé du clic.
  • KyoKyo Membre
    06:42 modifié #8
    Ah bah en fait c'est encore mieux avec les détails, ty pour ces explications  :)
Connectez-vous ou Inscrivez-vous pour répondre.