IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Flex Discussion :

Utilité des états de composants dans une logique MVC [Débat]


Sujet :

Flex

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Par défaut Utilité des états de composants dans une logique MVC
    bonjour,

    j'aimerais avoir votre opinion quant à l'utilité des states dans un composant personnalisé. Attention, j'ai bien dis "states" et non "skinStates".

    Je m'explique :

    il n'est plus à démontrer l'utilité du pattern MVC (Model, Vue, Controleur) et Flex via sa nouvelle architecture Spark permet de découper plus facilement un composant en ces 3 tiers (voir un article ici ). Ainsi, ces 3 tiers peuvent être représentés par le schéma ci dessous (source : lien ci dessus) :





    La Vue, représentée dorénavant dans l'architecture Spark par la classe skin associée au composant, doit donc encapsuler toute la logique visuelle du composant en question. Logique visuelle comprenant entre autre :

    • les couleurs
    • les fonts
    • le placement relatif des composants entre eux (top, left, bottom, right...)
    • la présence ou non de des composants (includeIn, excludeFrom)
    • les transitions
    • ...


    nota : j'appelle composants dans la liste non exhaustive ci dessus, les sous composants du composant personnalisé en question (ie : les fameux skinParts).

    Or, Flex via la classe UIComponent (classe parente de tous les composants personnalisés), définit une propriété "states" de type tableau initialisée à null mais pouvant facilement être modifiée via les getters/setters. Cette propriété sert à créer des états sur le composant lui même contrairement au skinStates définissant des états sur les skins.

    Cependant, nous sommes en droit de nous poser la question suivante : A quoi peut donc servir cette propriété states sur le composant personnalisé ?

    Par "facilité" ou par méconnaissance du pattern MVC, nous pourrions être tentés de basculer la logique vue dans la logique contrôleur et de faire dans le cas d'un composant personnalisé étendant la classe SkinnableContainer et nommé MyPersoComp la chose suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <?xml version="1.0" encoding="utf-8"?>
    <s:SkinnableContainer xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					  xmlns:s="library://ns.adobe.com/flex/spark" 
    					  xmlns:mx="library://ns.adobe.com/flex/mx">
     
    	<s:states>
    		<s:State name="state1" />
    		<s:State name="state2" />
    	</s:states>
     
    	<s:Button label.state1="label de l'état 1" label.state2="label de l'état 2" />
     
    </s:SkinnableContainer>
    Aucune erreur de compilation n'apparait et notre composant est opérationnel. Nous pouvons donc facilement instancier notre composant MyPersoComp et changer dynamiquement son label en jouant avec la propriété currentState du composant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    			   xmlns:s="library://ns.adobe.com/flex/spark" 
    			   xmlns:mx="library://ns.adobe.com/flex/mx"  xmlns:components="com.components.*">
     
    	<components:MyPersoComp currentState="state2" />
     
    </s:Application>
    Un joli bouton labellisé "label de l'état 2" apparaît ce qui est le résultat attendu. Oui mais en faisant cela, nous avons déplacé la logique vue (ici représenté par le changement/affichage du label bouton) dans le tiers contrôleur représenté par le composant...
    Cet exemple bien que simpliste montre la facilité avec laquelle un développeur peut se mélanger les pinceaux avec les notions de "component states" et de "skin states". Certes, l'exemple évoqué représente la façon la plus directe et la plus rapide pour modifier rapidement et automatiquement une propriété en fonction d'un état, mais comme souvent en informatique, la solution de facilité n'est pas la meilleure.

    Voici maintenant 2 solutions qui selon moi représentent une bien meilleure alternative :

    1ère solution : faire du "mirroring delegate"

    description: nous créons toujours 2 états state1 et state2 sur notre contrôleur (notre composant dérivé de UIComponent) puis nous surchargeons la méthode getCurrentSkinState() de ce composant afin de faire correspondre (mirroring) l'état de notre composant avec l'état de notre skin. Nous avons donc un tableau de states correspondant au tableau de skinStates ou du moins une relation 1-1 entre les states et les skinStates.

    MyPersoComp (version AS3):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
     
    package com.components
    {
     
    	import flash.events.MouseEvent;
     
    	import mx.events.FlexEvent;
    	import mx.states.State;
     
    	import spark.components.Button;
    	import spark.components.supportClasses.SkinnableComponent;
     
    	/**
    	 * notre skin devra contenir les skinStates "state1" et "state2" 
    	 */
    	[SkinState("state1")]
    	[SkinState("state2")]
     
    	public class MyPersoComp extends SkinnableComponent
    	{
    		/**
    		 * notre skin devra obligatoirement implémenter un bouton dont l'id vaudra "buttonPerso"
    		 */
    		[SkinPart(name="buttonPerso", required="true")]
    		public var buttonPerso:Button;		
     
    		public function MyPersoComp()
    		{
    			super();
    			initStates();
    			addEventListener(FlexEvent.STATE_CHANGE_COMPLETE, onStateChange);
    		}
     
    		/**
    		 * crée les états associés à notre composant
    		 */
    		private function initStates():void
    		{
    			var state1:State = new State();
    			state1.name = "state1";
     
    			var state2:State = new State();
    			state2.name = "state2";
     
    			states = [state1, state2];
    		}
     
    		/**
    		 * un changement de state implique une invalidation de notre skin.
    		 * Cette invalidation appelle (automatiquement) la fonction getCurrentSkinState() 
    		 * qui retourne le nouvel état à la skin
    		 */
    		private function onStateChange(e:FlexEvent):void
    		{
    			invalidateSkinState();
    		}
     
    		/**
    		* ajout d'un écouteur sur le bouton à l'ajout de celui dans notre composant personnalisé
    		*/
    		override protected function partAdded(partName:String, instance:Object):void
    		{
    			super.partAdded(partName, instance);
     
    			if(instance == buttonPerso)
    			{
    				buttonPerso.addEventListener(MouseEvent.CLICK, onClick);
    			}
    		}
     
    		/**
    		 * suppression de l'écouteur ajouté par la fonction partAdded
    		 */
    		override protected function partRemoved(partName:String, instance:Object):void
    		{
    			super.partRemoved(partName, instance);
     
    			if(instance == buttonPerso)
    			{
    				buttonPerso.removeEventListener(MouseEvent.CLICK, onClick);
    			}
    		}
     
    		/**
    		 * un click sur notre bouton change l'état de notre composant et finalement son label 
    		 * via la diffusion de l'événement FlexEvent.STATE_CHANGE_COMPLETE 
    		 */
    		protected function onClick(e:MouseEvent):void
    		{
    			currentState = (currentState == "state1" || null == currentState) ? "state2" : "state1";
    		}
     
    		/**
    		 * un changement d'état sur notre composant sera immédiatement affecté sur notre skin.
    		 * Cette fonction sert donc de mirroir(composant -> skin)
    		 */
    		override protected function getCurrentSkinState():String
    		{
    			return currentState;
    		}
     
    	}
    }
    Appel dans notre application :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <components:MyPersoComp skinClass="com.components.skins.MyPersoCompSkin1" />
    MyPersoCompSkin1 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    <?xml version="1.0" encoding="utf-8"?>
    <s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
    		xmlns:s="library://ns.adobe.com/flex/spark" 
    		xmlns:mx="library://ns.adobe.com/flex/mx">
     
    	<fx:Metadata>
    		[HostComponent("com.components.MyPersoComp")]
    	</fx:Metadata>
     
    	<s:states>
    		<s:State name="state1" />
    		<s:State name="state2" />		
    	</s:states>
     
    	<s:Button id="buttonPerso" label.state1="label de l'état 1"  label.state2="label de l'état 2"/>
     
     
    </s:Skin>
    Ainsi, notre composant personnalisé délégue toute sa logique vue à son skin via la diffusion d'un événement FlexEvent.STATE_CHANGE_COMPLETE.


    2ième solution : solution "stateless states"

    description: cette solution repose grandement sur le fondement de la solution précédente excepté que l'on s'abstient d'utiliser la propriété states de notre composant. On préférera utiliser une simple propriété de notre classe afin de stocker le nom de notre skinState (un peu moins consommateur de ressources donc).

    MyPersoComp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
     
    package com.components
    {
     
    	import flash.events.MouseEvent;
     
    	import mx.events.FlexEvent;
    	import mx.states.State;
     
    	import spark.components.Button;
    	import spark.components.supportClasses.SkinnableComponent;
     
    	/**
    	 * notre skin devra contenir les skinStates "state1" et "state2" 
    	 */
    	[SkinState("state1")]
    	[SkinState("state2")]
     
    	public class MyPersoComp extends SkinnableComponent
    	{
    		/**
    		 * notre skin devra obligatoirement implémenter un bouton dont l'id vaudra "buttonPerso"
    		 */
    		[SkinPart(name="buttonPerso", required="true")]
    		public var buttonPerso:Button;		
     
    		/**
    		 * notre propriété nous donnant le nom de notre skinState
    		 */
    		private var _state:String;
     
    		public function MyPersoComp()
    		{
    			super();		
    		}				
     
    		/**
    		* ajout d'un écouteur sur le bouton à l'ajout de celui dans notre composant personnalisé
    		*/
    		override protected function partAdded(partName:String, instance:Object):void
    		{
    			super.partAdded(partName, instance);
     
    			if(instance == buttonPerso)
    			{
    				buttonPerso.addEventListener(MouseEvent.CLICK, onClick);
    			}
    		}
     
    		/**
    		 * suppression de l'écouteur ajouté par la fonction partAdded
    		 */
    		override protected function partRemoved(partName:String, instance:Object):void
    		{
    			super.partRemoved(partName, instance);
     
    			if(instance == buttonPerso)
    			{
    				buttonPerso.removeEventListener(MouseEvent.CLICK, onClick);
    			}
    		}
     
    		/**
    		 * un click sur notre bouton change l'état de notre composant via une invalidation de son skin
    		 */
    		protected function onClick(e:MouseEvent):void
    		{
    			_state = (_state == "state1" || null == _state) ? "state2" : "state1";
    			invalidateSkinState();
    		}
     
    		/**
    		 * la valkeur de notre skinState est contenu dans notre propriété privée _state
    		 */
    		override protected function getCurrentSkinState():String
    		{
    			return _state;
    		}
     
    	}
    }

    Appel dans notre application :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    idem méthode précédente

    MyPersoCompSkin1 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    idem méthode précédente


    Les limites de ces 2 solutions : les solutions évoquées sont parfaitement fonctionnelles si le composant personnalisé ne comprend uniquement que des sous composants de type "skinPart" c'est à dire qu'il faut que tous ces sous composants soient listés via la metadata [SkinPart] dans notre fichier .as. A défaut, si nous ajoutons des composants non annotés par cette metadata dans notre composant (ce qui est loin d'être recommandé ni recommandable je pense), il nous faudra faire un "mix" entre la première solution proposée et une notation pointée dans notre composant (propriete.state1 = "..."). Nous retombons donc dans les travers évoqués en début de sujet.


    Cette vision des choses n'engage que moi bien sûr. De mon côté, je n'utilise jamais les états de composants ou du moins je n'utilise jamais de notation pointée directement dans mes composants personnalisés. La seule façon dont j'utilise les états de composants est celle évoquées ici en méthode 1. Ceci dit, j'utilise la méthode 2 autant que faire se peut.


    ...et vous, comment voyez vous les choses concernant les "components states" ?



    Merci de m'avoir lu et de votre participation au débat

  2. #2
    Membre Expert Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Par défaut
    Personne n'utilise de composants personnalisés ici ?

  3. #3
    Membre Expert
    Avatar de Jim_Nastiq
    Homme Profil pro
    Architecte, Expert Flex
    Inscrit en
    Avril 2006
    Messages
    2 335
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte, Expert Flex
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2006
    Messages : 2 335
    Par défaut
    Salut,

    alors pour moi les states dont tu parles je les utilises comme des états des vues et les autres comme des états des skins. Oui je sépare le skin de la vue. Une vue est un container et un skin est l'habillage du container.

    Typiquement j'ai un stack de vue dans un container parent avec pour chacune des vues un state. Et chacune de ces vues à un skin avec des skinState.

    Par exemple, le skinState peut etre un habillage particulier si l'utilisateur est une femme ou bien un homme. (j'ai donc 2 skinStates 'menState' et 'womenState') et dans mon container des states pour chacune des vues ('loginState', 'viewOneState', ...)

    les states sont donc plus 'métier'

    Pensez vraiment à effectuer une recherche avant de poster, ici et sur un moteur de recherche! c'est la moindre des choses
    Pensez au tag

    Mon Blog sur la techno Flex
    Ma page sur Developpez.com

    Jim_Nastiq

  4. #4
    Membre Expert Avatar de Madfrix
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    2 326
    Détails du profil
    Informations personnelles :
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 326
    Par défaut
    Salut,

    ça se défend ton point de vue c'est vrai. Finalement, pour un composant basique à ce que je comprends, tu n'utilises pas de skin en fait c'est ça ? Tout est externalisé en quelque sorte dans ce que tu appelles la vue ?

    Pour toi, un skin te permet de "simplifier" le code des states il me semble non ?
    Par exemple, pour un composant nommé Horloge affichant l'heure et un Label "vous êtes connecté" si on est connecté et rien sinon tu procéderas ainsi ?

    2 états de vue :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <s:states>
    	<s:State name="connecte" />
    	<s:State name="non_connecte" />
    </s:states>
    Maintenant, si on veut un fond de couleur différente si connecté en tant que femme ou homme, dans ton cas de figure, tu procéderas ainsi si j'ai bien compris ?

    2 états de vue :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <s:states>
    	<s:State name="connecte" />
    	<s:State name="non_connecte" />
    </s:states>
    2 états de skin
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <s:states>
    	<s:State name="homme" />
    	<s:State name="femme" />
    </s:states>
    au lieu éventuellement de procéder de la sorte :

    4 états de vue :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    <s:states>
            <s:State name="homme_connecte" />
    	<s:State name="homme_non_connecte" />
    	<s:State name="femme_connecte" />
    	<s:State name="femme_non_connecte" />
    </s:states>
    Dans ce cas de figure, tu as donc 2*2 états si on peut dire au lieu de 4 états ce qui change pas grand chose donc. Par contre, dans le cas de figure où on a les états "connecte", "non_connecte", "presque_connecte" et "en_train_de_se_connecter" (désolé pour l'exemple...) au lieu d'avoir 4*2=8 states de vue tu feras 4+2=6 states si tu gardes les états homme et femme dans le skin.

    J'ai bien résumé/compris ton point de vue ou non ?

    Merci de ta participation en tout cas

Discussions similaires

  1. gerer la position des composants dans une fenetre
    Par AbouraStat dans le forum AWT/Swing
    Réponses: 2
    Dernier message: 13/05/2009, 11h37
  2. Insertion des composants dans une JPanel
    Par altaro dans le forum Débuter
    Réponses: 3
    Dernier message: 11/05/2009, 19h25
  3. Intègrer des composants dans une jTable
    Par programaniac dans le forum Débuter
    Réponses: 2
    Dernier message: 30/03/2009, 09h53
  4. Répartition des composants dans une fenetre
    Par Oussama_Gabes dans le forum AWT/Swing
    Réponses: 7
    Dernier message: 11/04/2008, 14h40
  5. Centrer des composants dans une form?
    Par alg_dev dans le forum Delphi
    Réponses: 1
    Dernier message: 10/06/2007, 10h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo