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

WinDev Discussion :

[POO] Mapping, Databinding, MVP architecture - Questions


Sujet :

WinDev

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2020
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2020
    Messages : 66
    Points : 35
    Points
    35
    Par défaut [POO] Mapping, Databinding, MVP architecture - Questions
    Bonsoir,

    Depuis quelques semaines je m'intéresse à la POO afin de structurer mon projet, séparer les couches UI/Métier, etc.

    Selon ma logique et après pas mal de recherche, je souhaiterais partir sur l'architecture suivante : Fenêtre <-> Classe Contrôleur/Presentation propre à ma fenêtre <-> Classes modèles mappées sur mes fichiers de données <-> BDD

    Question préalable : Est-ce bien une architecture MVP ? J'ai analysé en profondeur les deux exemples fournis par PCSOFT MVP1/MVP2. Cela ressemble à ça, sauf qu'eux passent les couches présentations en paramètre dans les fenêtres et travaillent pratiquement qu'en mémoire, pas de rafraîchissement de tables etc. Moi je souhaite passer les ID dans mes fenêtres afin d'actualiser les données, recharger mes tables afin d'avoir des données toujours à jour (dans le cas où d'autres utilisateurs auraient modifiés des données entre temps etc.)


    Cas concret : (J'ai pris cet exemple là afin d'avoir un exemple concret, je réutiliserais le même principe pour mes autres fenêtres)

    J'ai une table société, une table société_probleme, une table probleme_contact et une table contact. Je m'intéresse à la partie Societe_Probleme, Probleme_Contact. Pour un problème donné, je peux associer plusieurs contacts à ce problème. J'ai donc :

    Nom : Probleme.jpg
Affichages : 3773
Taille : 172,7 Ko


    J'ai généré mes classes MProbleme, MProbleme_Contact et MContact

    Classe MProbleme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    MProbleme est une Classe <MAPPING=Societe_Probleme>
    hérite de MBase
    
    <MAPPING>
    m_nPBid			est un entier sur 8 octets				<MAPPING=DRPBid>
    m_nPBcode			est un entier sans signe sur 4 octets	<MAPPING=DRPBcode, clé unique>
    m_nPBnom est une chaine <MAPPING=PBnom>
    m_sPBdescript		est une chaîne ANSI						<MAPPING=DRPBdescript>
    m_dPBdate			est une Date							<MAPPING=DRPBdate>
    <FIN>
    	
    m_tabProblemeContact est un tableau de MProbleme_Contact dynamique //Dois-je déclarer ici le tableau ? (Plusieurs contacts sont associés au problème)
    
    FIN
    Possède la méthode ChargeElement pour charger le problème, les méthodes d'enregistrement etc.


    Classe MProbleme_Contact :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    MProbleme_Contact est une Classe <MAPPING=Probleme_Contact>
    hérite de MBase
    <MAPPING>
    m_nPBCONTid est un entier <MAPPING=PBCONTid, clé unique>
    m_nPBcode		est un entier   <MAPPING=PBcode>
    m_nCONTcode		est un entier   <MAPPING=CONTcode>
    m_sPBCONTcom 	est une chaine <MAPPING=PBCONTcom>
    <FIN>
     
    m_pclContact est un MContact dynamique
    FIN
    Possède la méthode : tabCharge qui renvoie un tableau de contact pour un problème donné


    J'ai également créé une classe PFicheProbleme qui gère les actions des utilisateurs sur la fenêtre, initialise les données en appelant les méthodes des classes MProbleme et MProbleme_Contact etc.

    Classe Controleur/Présentation PFicheProbleme :
    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
    PFicheProbleme est une Classe	
    	m_pclProbleme				est un MProbleme dynamique <associé>	//classe Problème, possède un tuple tableau contact
    	m_pclProblemeContact		est un MProbleme_Contact dynamique			//Élément courant du tableau Contact
    FIN
    
    Constructeur(PBcode)
    m_pclProbleme <- allouer un MProbleme(PBcode)
    m_pclProblemeContact <- allouer un MProblemeContact(PBcode)
    
    Méthode ChargeProbleme // qui va appeler la méthode de la classe MProbleme :
    Si m_pclProbleme.bHChargeElement()=faux alors
    erreurdéclenche(2,...)
    renvoyer faux
    fin
    demandemiseajourui()
    renvoyer vrai
    
    Méthode ChargeTableProblemeContact // qui va remplir mon tableau de mon objet à partir de la méthode de la classe MProblemeContact
    m_pclProbleme.m_tabProblemeContact = m_pclProblemeContact.tabCharge()
    demandemiseajourui("CONTACT")

    Maintenant mes questions concernant tout ça :

    Q1) Déjà est-ce que la façon de faire est bonne, propre ?

    Q2) Dois-je déclarer le tableau : m_tabProblemeContact est un tableau de MProbleme_Contact dynamique directement dans ma classe MProbleme comme j'ai fait OU alors le déclarer dans ma classe présentation PFicheProbleme et le remplir indépendamment de mon objet Probleme ? (Ligne en rouge dans mon exemple)

    Q3) Si je conserve "m_tabProblemeContact est un tableau de MProbleme_Contact dynamique" dans ma classe MProbleme. Est-ce que je dois créer dans ma classe MProbleme une méthode "ChargeTableauProblemeContact" qui va exécuter la méthode TabCharge (Renvoie un tableau) de ma classe MProbleme_Contact afin de remplir le tableau ? Ou alors j'appelle directement ma méthode TabCharge depuis ma classe Présentation PFicheProbleme comme j'ai fait dans mon exemple ci-dessus (Code en vert)

    Q4) Dans le cas concret que j'ai exposé ci-dessus, si je veux ajouter une ligne à mon tableau m_tabProblemeContact de ma classe MProbleme (tableau de MProblemeContact) , où dois-je écrire cette méthode ?
    - Dans ma classe Présentation ?
    - Dans ma classe MProblemeContact (où j'ai la méthode pour me renvoyer un tableau) en passant mon tableau en paramètre de ma méthode ? Dans ce cas, je dois appeler la méthode à partir de ma classe présentation ? Ou je dois dans ma classe MProbleme écrire aussi une méthode "Ajouter Ligne" qui va chercher la méthode dans MProblemeContact afin d'ajouter la ligne à son propre tableau membre ?
    - Ou alors créer une classe indépendante "tableau" de Probleme_Contact :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MTableauProbleme_Contact est une classe
    m_tabProblemeContact est un tableau de MProbleme_Contact dynamique
    avec les méthodes de chargement, d'ajout, modification, et suppression de ligne
    et ainsi remplacer dans ma classe MProbleme : "m_tabProblemeContact est un tableau de MProbleme_Contact dynamique" PAR "m_pclProblemeContact est un MTableauProbleme_Contact dynamique" et utiliser directement les méthodes de cette classe pour charger mon tableau, ajouter ligne etc


    J'espère m'être fait comprendre, pas facile à l'écrit


    Merci d'avance à ceux qui prendront le temps de m'aider ou me donner leur avis

    Bonne soirée,

    Esteban

  2. #2
    Membre actif

    Homme Profil pro
    Sans
    Inscrit en
    Mars 2018
    Messages
    153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Mars 2018
    Messages : 153
    Points : 295
    Points
    295
    Billets dans le blog
    1
    Par défaut
    Bonjour Djsven,

    Je vais tenter de répondre à tes interrogations même si mon expérience avec Windev ne remonte pas à plus de 8 à 10 mois et que certainement des personnes plus expérimentées pourr-aient-ont apporter un avis plus professionnel.

    Citation Envoyé par Djsven Voir le message
    Bonsoir,

    Depuis quelques semaines je m'intéresse à la POO afin de structurer mon projet, séparer les couches UI/Métier, etc.

    Selon ma logique et après pas mal de recherche, je souhaiterais partir sur l'architecture suivante : Fenêtre <-> Classe Contrôleur/Presentation propre à ma fenêtre <-> Classes modèles mappées sur mes fichiers de données <-> BDD

    Question préalable : Est-ce bien une architecture MVP ? J'ai analysé en profondeur les deux exemples fournis par PCSOFT MVP1/MVP2. Cela ressemble à ça, sauf qu'eux passent les couches présentations en paramètre dans les fenêtres et travaillent pratiquement qu'en mémoire, pas de rafraîchissement de tables etc. Moi je souhaite passer les ID dans mes fenêtres afin d'actualiser les données, recharger mes tables afin d'avoir des données toujours à jour (dans le cas où d'autres utilisateurs auraient modifiés des données entre temps etc.)

    ...
    Tu n'es pas obligé de passer la classe de présentation en paramètre à ta fenêtre. Il suffit de déclarer dans la partie déclarations globales :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     goPresentation est une classeDePresentationDeMaFenetre dynamique <présentation> <- allouer une classeDePresentationDeMaFenetre

    Citation Envoyé par Djsven Voir le message
    ...

    Maintenant mes questions concernant tout ça :

    Q1) Déjà est-ce que la façon de faire est bonne, propre ?
    C'est un bon début que de vouloir mettre en place un développement basé sur le pattern MVP. D'après la doc PCSoft : https://doc.pcsoft.fr/fr-FR/?1000021496, ton architecture correspond, peu ou prou, à une architecture MVP.

    Cependant, AMHA, tes "classes modèles mappées sur mes fichiers de données" doivent rester atomiques. J'entends par là qu'elles ne doivent représenter qu'un seul enregistrement de ta table (ton fichier en langage windev) et grâce à l'héritage de la classe MBase, être manipulée avec les méthodes Charger(), bEnregistrer() et Supprimer().

    J'intercalerai, entre ta classe "présentation" et ta classe "modèle mappées sur mes fichiers de données" une couche, que l'on pourrait appeler "façade" supplémentaire qui se chargerait de :
    - Manipuler une ou plusieurs classes mappées aux fichiers. La logique métier de ton application.
    - Proposer à ta couche présentation une API simple permettant de s'affranchir de la complexité des relations existantes entre les différentes classes de mapping


    Citation Envoyé par Djsven Voir le message

    Q2) Dois-je déclarer le tableau : m_tabProblemeContact est un tableau de MProbleme_Contact dynamique directement dans ma classe MProbleme comme j'ai fait OU alors le déclarer dans ma classe présentation PFicheProbleme et le remplir indépendamment de mon objet Probleme ? (Ligne en rouge dans mon exemple)

    Q3) Si je conserve "m_tabProblemeContact est un tableau de MProbleme_Contact dynamique" dans ma classe MProbleme. Est-ce que je dois créer dans ma classe MProbleme une méthode "ChargeTableauProblemeContact" qui va exécuter la méthode TabCharge (Renvoie un tableau) de ma classe MProbleme_Contact afin de remplir le tableau ? Ou alors j'appelle directement ma méthode TabCharge depuis ma classe Présentation PFicheProbleme comme j'ai fait dans mon exemple ci-dessus (Code en vert)
    Dans la classe "classeDePresentationDeMaFenetre ", je déclarerais une variable publique qui serait un tableau de MProblemeContact (ou peut-être même de cProblèmeContact :<= la façade) qui serait bindé à la table qui sert à l'affichage.

    Citation Envoyé par Djsven Voir le message
    Q4) Dans le cas concret que j'ai exposé ci-dessus, si je veux ajouter une ligne à mon tableau m_tabProblemeContact de ma classe MProbleme (tableau de MProblemeContact) , où dois-je écrire cette méthode ?
    - Dans ma classe Présentation ?
    - Dans ma classe MProblemeContact (où j'ai la méthode pour me renvoyer un tableau) en passant mon tableau en paramètre de ma méthode ? Dans ce cas, je dois appeler la méthode à partir de ma classe présentation ? Ou je dois dans ma classe MProbleme écrire aussi une méthode "Ajouter Ligne" qui va chercher la méthode dans MProblemeContact afin d'ajouter la ligne à son propre tableau membre ?
    - Ou alors créer une classe indépendante "tableau" de Probleme_Contact :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MTableauProbleme_Contact est une classe
    m_tabProblemeContact est un tableau de MProbleme_Contact dynamique
    avec les méthodes de chargement, d'ajout, modification, et suppression de ligne
    et ainsi remplacer dans ma classe MProbleme : "m_tabProblemeContact est un tableau de MProbleme_Contact dynamique" PAR "m_pclProblemeContact est un MTableauProbleme_Contact dynamique" et utiliser directement les méthodes de cette classe pour charger mon tableau, ajouter ligne etc


    J'espère m'être fait comprendre, pas facile à l'écrit


    Merci d'avance à ceux qui prendront le temps de m'aider ou me donner leur avis

    Bonne soirée,

    Esteban
    Pour ajouter une ligne dans ton tableau, il te faut une autre fenêtre avec sa classe de présentation associée qui se chargerait de l'ajout.
    Pour supprimer une ligne dans ton tableau, il te faut une autre fenêtre avec sa classe de présentation associée qui se chargerait de la suppression.

    Je rajouterai que, à mon sens, l'attribut <associé> doit être à proscrire car il va à l'encontre de la notion d'encapsulation.

    hth,
    Padbrain.

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2020
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2020
    Messages : 66
    Points : 35
    Points
    35
    Par défaut
    Salut Padbrain, merci d'avoir pris le temps de me répondre,

    Citation Envoyé par padbrain Voir le message
    J'intercalerai, entre ta classe "présentation" et ta classe "modèle mappées sur mes fichiers de données" une couche, que l'on pourrait appeler "façade" supplémentaire qui se chargerait de :
    - Manipuler une ou plusieurs classes mappées aux fichiers. La logique métier de ton application.
    - Proposer à ta couche présentation une API simple permettant de s'affranchir de la complexité des relations existantes entre les différentes classes de mapping
    Je voyais justement la classe "Présentation" comme la couche façade me permettant justement d'y faire mes traitements. Avant de penser à cette classe intermédiaire, je manipulais mes actions avec mes classes modèles dans des procédures locales/globales, c'est pour cela que je me suis dit que ça serait sans doute plus propre d'ajouter cette classe intermédiaire qui, selon les actions utilisateurs cotés UI, me permettrait de manipuler mes classes modèles. C'est pour cela aussi que je m'y perd un peu, à savoir si je dois plutôt écrire ma méthode coté classe modèle, ou plutôt coté classe intermédiaire "présentation". Je reprendrais dans la suite de mon message l'exemple de l'ajout d'une ligne à un tableau que j'ai récupéré à partir d'une méthode d'une classe.



    Citation Envoyé par padbrain Voir le message
    Dans la classe "classeDePresentationDeMaFenetre ", je déclarerais une variable publique qui serait un tableau de MProblemeContact (ou peut-être même de cProblèmeContact :<= la façade) qui serait bindé à la table qui sert à l'affichage.
    C'est ce que je faisais aussi, mais étant donné que justement cette variable tableau de MProbleme_Contact est liée à ma classe MProbleme (Un problème peut avoir un ou plusieurs contacts associés, donc mes contacts sont liés à un problème), n'est-ce pas plus propre de définir directement ma variable Tableau de MProbleme_Contact comme un membre de ma classe MProbleme ?

    Donc dans ma classe présentation, plutôt que déclarer ma variable tableau en plus de ma variable objet MProbleme, je déclarerais simplement la variable qui est un objet de MProbleme qui possèdes les membres mappés sur mon fichier d'analyse et qui possède également le tableau de ma classe MProbleme_Contact (Ce dernier sera databindé sur ma table visuelle coté UI). Et j'accéderais au tableau de contacts associés par MonObjProbleme.MontableauDeMProblemeContact ?



    Citation Envoyé par padbrain Voir le message
    Pour ajouter une ligne dans ton tableau, il te faut une autre fenêtre avec sa classe de présentation associée qui se chargerait de l'ajout.
    Pour supprimer une ligne dans ton tableau, il te faut une autre fenêtre avec sa classe de présentation associée qui se chargerait de la suppression.
    Pour les ajouts et suppressions de ligne, je parlais dans la variable publique de mon tableau d'objet, quand j'ai déjà ouvert la fiche pour ajouter et que je valide. 90% du temps après un ajout en bdd je recharge toute la table visuelle afin d'avoir des données à jour, dans ce cas pas de problème je rappelle simplement ma méthode tabCharge de ma classe correspondante. Mais pour certains cas, je dois simplement ajouter une ligne à ma table visuelle sans tout rafraichir (Équivalent d'un tableAjouteLigne()), donc ici un tableauajoute(Tableau mémoire, Objet mémoire) + un tableAffiche() vu que ma table visuelle est databindée sur ma variable tableau.


    Comme j'ai dit plus haut, je m'y perd pour savoir où je dois déclarer ma méthode pour ajouter une ligne à une variable tableau d'objets récupérée par la méthode tabCharge de ma classe souhaitée.
    Par exemple, je prends l'exemple de ma classe MProbleme_Contact qui possède une méthode tabCharge pour me renvoyer un tableau de tout les contacts pour un problème donné. Admettons que j'ai créé une variable "TableauProblemeContact" qui est un tableau de MProbleme_Contact, j'utilise donc ma méthode tabCharge de MProbleme_Contact afin de le remplir. Là c'est ok.

    Mais maintenant, si je veux ajouter une ligne (ou supprimer peu importe) à cette variable "TableauProblemeContact", dois-je avoir une méthode dans ma classe MProbleme_Contact "tabAjoute" et passer en paramètre de ma méthode la variable tableau de MProbleme_Contact avec comme code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    PROCÉDURE tabAjoute(tabContact est un tableau de MProbleme_Contact) <métier>
    TableauAjoute(tabContact ,objet)
    et donc pour ajouter une ligne à cette variable tableau, j'instancie un objet (exemple : UnPbContact) de MProbleme_Contact que je remplie, et j'appelle ensuite la méthode tabAjoute en passant en paramètre le tableau :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    UnPbContact.tabAjoute(TableauProblemeContact)
    Ou alors ça ne se fait pas d'avoir dans la même classe, à la fois une méthode qui va me renvoyer un tableau de cette classe, et des méthodes qui vont me permettre d'ajouter, supprimer des lignes au tableau, sachant que ce tableau n'est pas directement un membre de ma classe (donc obligé de le passer en paramètre comme j'ai fait ci-dessus) ?


    Ou 2ème possibilité toujours dans l'exemple d'ajouter une ligne à cette variable tableau, je dois faire cet ajout directement dans ma classe présentation via la méthode :
    UnPbContact est un MProbleme_Contact, je remplie cet objet et ensuite je fais un tableauajoute(TableauProblemeContact,UnPbContact) (toujours dans la méthode de la classe Présentation)


    Ou 3eme possibilité, je créé une classe indépendante MTableauDeMProbleme_Contact qui possède comme membre un tableau de ma classe avec dedans les méthodes tabAjoute, tabSupprime, tabCharge :
    MTableauDeProbleme_Contact est une classe
    m_tabProblemeContact est un tableau de MProbleme_Contact dynamique

    et dans ce cas coté classe présentation, je déclarerais un objet (exemple MonObjet) de MTableauDeMProbleme_Contact et appellerait ses méthodes pour charger, ajouter ligne etc :
    MonObjet.tabAjoute(UnPbContact) //UnPbContact = Un objet de MProbleme_Contact

    Cette 3ème possibilité est utilisé dans l'exemple MVP de PCSOFT mais oblige pour chaque fichier de l'analyse de créer en plus de la classe mappée une classe Tableau de cette classe mappée. Donc 2 classes par fichier, pas top top...


    Cordialement,
    Esteban

  4. #4
    Membre actif

    Homme Profil pro
    Sans
    Inscrit en
    Mars 2018
    Messages
    153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Mars 2018
    Messages : 153
    Points : 295
    Points
    295
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Djsven Voir le message
    Salut Padbrain, merci d'avoir pris le temps de me répondre,



    Je voyais justement la classe "Présentation" comme la couche façade me permettant justement d'y faire mes traitements. Avant de penser à cette classe intermédiaire, je manipulais mes actions avec mes classes modèles dans des procédures locales/globales, c'est pour cela que je me suis dit que ça serait sans doute plus propre d'ajouter cette classe intermédiaire qui, selon les actions utilisateurs cotés UI, me permettrait de manipuler mes classes modèles. C'est pour cela aussi que je m'y perd un peu, à savoir si je dois plutôt écrire ma méthode coté classe modèle, ou plutôt coté classe intermédiaire "présentation". Je reprendrais dans la suite de mon message l'exemple de l'ajout d'une ligne à un tableau que j'ai récupéré à partir d'une méthode d'une classe.
    Lorsque je parle de façade, je pense surtout au patron de conception éponyme : https://fr.wikipedia.org/wiki/Fa%C3%...de_conception).

    Dans ce diagramme, les Packages correspondent à tes classes de mapping et les clients correspondent aux classes de présentation.

    Rien ne t'empêche d'utiliser tes classes de présentation comme une façade (bien que ce ne soit pas leur rôle) mais lorsque tu manipuleras des notions métier plus complexes qui demanderont une utilisation conjointe de tes classes modèles, l'implémentation de façades te facilitera grandement la tâche ET la maintenance.

    C'est ce que je faisais aussi, mais étant donné que justement cette variable tableau de MProbleme_Contact est liée à ma classe MProbleme (Un problème peut avoir un ou plusieurs contacts associés, donc mes contacts sont liés à un problème), n'est-ce pas plus propre de définir directement ma variable Tableau de MProbleme_Contact comme un membre de ma classe MProbleme ?

    Donc dans ma classe présentation, plutôt que déclarer ma variable tableau en plus de ma variable objet MProbleme, je déclarerais simplement la variable qui est un objet de MProbleme qui possèdes les membres mappés sur mon fichier d'analyse et qui possède également le tableau de ma classe MProbleme_Contact (Ce dernier sera databindé sur ma table visuelle coté UI). Et j'accéderais au tableau de contacts associés par MonObjProbleme.MontableauDeMProblemeContact ?
    En ce qui me concerne, je ne mettrai pas de membre Tableau de MProbleme_Contact dans la classe MProbleme. MProbleme est, pour moi, destinée uniquement à faire un CRUD sur le fichier Problème.

    Pour les ajouts et suppressions de ligne, je parlais dans la variable publique de mon tableau d'objet, quand j'ai déjà ouvert la fiche pour ajouter et que je valide. 90% du temps après un ajout en bdd je recharge toute la table visuelle afin d'avoir des données à jour, dans ce cas pas de problème je rappelle simplement ma méthode tabCharge de ma classe correspondante. Mais pour certains cas, je dois simplement ajouter une ligne à ma table visuelle sans tout rafraichir (Équivalent d'un tableAjouteLigne()), donc ici un tableauajoute(Tableau mémoire, Objet mémoire) + un tableAffiche() vu que ma table visuelle est databindée sur ma variable tableau.


    Comme j'ai dit plus haut, je m'y perd pour savoir où je dois déclarer ma méthode pour ajouter une ligne à une variable tableau d'objets récupérée par la méthode tabCharge de ma classe souhaitée.
    Par exemple, je prends l'exemple de ma classe MProbleme_Contact qui possède une méthode tabCharge pour me renvoyer un tableau de tout les contacts pour un problème donné. Admettons que j'ai créé une variable "TableauProblemeContact" qui est un tableau de MProbleme_Contact, j'utilise donc ma méthode tabCharge de MProbleme_Contact afin de le remplir. Là c'est ok.
    Si tu as une classe cProblemeContacts, tu peux déclarer dans celle-ci un tableau de MProbleme_Contact que tu alimenterais via une requête SELECT, comme tu dois déjà le faire.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    cProblemeContacts est une Classe
     
    	PRIVÉ
    		_tabProblemeContacts est un tableau de MProbleme_contact
    FIN
    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
     
    PROCÉDURE chargerProblemeContact(pIdProbleme est entier)
     
    requete est une chaîne = [
    	SELECT
    		patati,
    		patata
    	FROM
    		MProblemeContact
    	WHERE
    		idProbleme = %1 
    ]
     
    requete = ChaîneConstruit(requete, pIdProbleme)
     
    resultat est une Source de Données
    HExécuteRequête(requete, hRequêteDéfaut, resultat)
     
    POUR TOUT resultat 
    	_tabProblemeContacts.Ajoute(resultat)
    FIN
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    PROCÉDURE getProblemeContacts()
    RENVOYER _tabProblemeContacts

    Ta classe de présentation peut ensuite utiliser cette classe pour récupérer tous les contacts liés à un problème. Tu isoles ainsi la couche persistence de la couche IHM. La classe de présentation est, pour moi, liée à l'IHM.


    Mais maintenant, si je veux ajouter une ligne (ou supprimer peu importe) à cette variable "TableauProblemeContact", dois-je avoir une méthode dans ma classe MProbleme_Contact "tabAjoute" et passer en paramètre de ma méthode la variable tableau de MProbleme_Contact avec comme code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    PROCÉDURE tabAjoute(tabContact est un tableau de MProbleme_Contact) <métier>
    TableauAjoute(tabContact ,objet)
    et donc pour ajouter une ligne à cette variable tableau, j'instancie un objet (exemple : UnPbContact) de MProbleme_Contact que je remplie, et j'appelle ensuite la méthode tabAjoute en passant en paramètre le tableau :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    UnPbContact.tabAjoute(TableauProblemeContact)
    Ou alors ça ne se fait pas d'avoir dans la même classe, à la fois une méthode qui va me renvoyer un tableau de cette classe, et des méthodes qui vont me permettre d'ajouter, supprimer des lignes au tableau, sachant que ce tableau n'est pas directement un membre de ma classe (donc obligé de le passer en paramètre comme j'ai fait ci-dessus) ?


    Ou 2ème possibilité toujours dans l'exemple d'ajouter une ligne à cette variable tableau, je dois faire cet ajout directement dans ma classe présentation via la méthode :
    UnPbContact est un MProbleme_Contact, je remplie cet objet et ensuite je fais un tableauajoute(TableauProblemeContact,UnPbContact) (toujours dans la méthode de la classe Présentation)
    Attention, ajouter ou supprimer une ligne dans ta variable n'influe pas sur le contenu de ton fichier. Tu t'exposes, alors, à ne plus avoir ton tableau de visualisation en phase avec le contenu de ton fichier en base.


    Ou 3eme possibilité, je créé une classe indépendante MTableauDeMProbleme_Contact qui possède comme membre un tableau de ma classe avec dedans les méthodes tabAjoute, tabSupprime, tabCharge :
    MTableauDeProbleme_Contact est une classe
    m_tabProblemeContact est un tableau de MProbleme_Contact dynamique

    et dans ce cas coté classe présentation, je déclarerais un objet (exemple MonObjet) de MTableauDeMProbleme_Contact et appellerait ses méthodes pour charger, ajouter ligne etc :
    MonObjet.tabAjoute(UnPbContact) //UnPbContact = Un objet de MProbleme_Contact

    Cette 3ème possibilité est utilisé dans l'exemple MVP de PCSOFT mais oblige pour chaque fichier de l'analyse de créer en plus de la classe mappée une classe Tableau de cette classe mappée. Donc 2 classes par fichier, pas top top...
    C'est, je crois ce que je te propose. Cependant, tu n'es pas obligé de créer une "classe tableau" associée pour toutes tes classes mappées si tu n'en as pas besoin dans ton application .
    Si tu veux séparer tes couches, il faut faire ce qu'il faut

    A mon sens, il te faut une fenêtre pour ajouter un contact et une pour supprimer un contact. Une fois une de ces actions faite, tu peux recharger ta visu tableau à partir de ta requête et ta visu sera alors en phase avec le contenu de ton fichier.
    Ces vues manipulent des MProbleme_contact en CRUD.

    hth,
    Padbrain

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2020
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2020
    Messages : 66
    Points : 35
    Points
    35
    Par défaut
    Merci pour ta réponse,

    Par contre, pour ma table visuelle de Probleme_Contact sur ma fenêtre Problème, j'ai besoin d'avoir également les colonnes avec le nom du contact (table contact), le prénom du contact (table contact) et le nom de la société du contact (table societe). Or je n'ai pas ça dans ma table jointure Probleme_Contact. J'ai donc fait une requête avec jointure. Mais vu que dans ma table de jointure Probleme_Contact je n'ai que l'id du contact, l'id du probleme et le commentaire, j'ai ajouté un membre objet de MContact à ma classe MProbleme_Contact (voir ci dessous), et dans ma classe MContact un membre objet de MSociete. Je remplie ainsi mon tableau de MProbleme_Contact via des fichiersversmémoire successif (objet MProbleme_Contact, sous objet MContact et sous sous objet MSociete) (Voir ci-dessous).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    MProbleme_Contact est une Classe <MAPPING=Probleme_Contact>
    	hérite de MBase
    	<MAPPING>
    	m_nPBcode		est un entier sans signe sur 4 octets	<MAPPING=PBcode>
    	m_sCONTcode		est une chaîne ANSI						<MAPPING=CONTcode>
    	m_sPBCNTcom	est une chaîne ANSI						<MAPPING=PBCNTcom>
    	<FIN>
    	
    	m_pclContact est un MContact dynamique //MContact quant-à lui possède un membre m_pclSociete est un MSociete dynamique
    FIN
    Ma classe MTableauDeProbleme_Contact (équivalent à ton cProblemeContact) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    MTableauDeProbleme_Contact est une Classe
    	m_tabContact		est un tableau de MProbleme_Contact dynamique
    FIN
    qui possède une méthode pour charger le tableau :

    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
    PROCÉDURE chargerProblemeContact(nProblemeCode,clErreur est un CErreur) <métier>
     
    SupprimeTout(m_tabContact)
     
    MaRequete est une Requête SQL = 
    [
    	SELECT 
    	Probleme_Contact.PBcode AS PBcode,
    	Probleme_Contact.CONTcode AS CONTcode,
    	Probleme_Contact.PBCNTcom AS PBCNTcom,
    	Contact.CONTnom AS CONTnom,
    	Contact.CONTprenom AS CONTprenom,
    	Societe.SOCnom AS SOCnom
    	FROM Probleme_Contact
    	INNER JOIN Contact ON Probleme_Contact.CONTcode	= Contact.CONTcode
    	LEFT OUTER JOIN Societe ON Contact.SOCcode = Societe.SOCcode
    	WHERE Probleme_Contact.PBcode = {ParamPBcode}
    	ORDER BY CONTnom ASC, CONTprenom ASC
    ]
     
    MaRequete.ParamPBcode = nProblemeCode
    SI PAS HExécuteRequête(MaRequete,hRequêteDéfaut) ALORS 
    	clErreur.Init(CErreur::ErreurSQL)
    	RENVOYER Faux
    FIN
    POUR TOUT MaRequete 
    	clUnContact est un MProbleme_Contact
    	clUnContact.m_pclContact <- allouer un MContact
    	clUnContact.m_pclContact.m_pclSociete <- allouer un Msociete
    	clUnContact.ChargeElement(MaRequete..Nom) //Equivalent à WL.FichierVersMémoire(clUnContact,MaRequete..Nom)
    	WL.FichierVersMémoire(clUnContact.m_pclContact,MaRequete..Nom)
    	WL.FichierVersMémoire(clUnContact.m_pclContact.m_pclSociete,MaRequete..Nom)
    	TableauAjoute(m_tabContact,clUnContact)
    FIN
    HLibèreRequête(MaRequete)

    Est-ce que cela se fait ? Je rappelle que ma table Probleme_Contact est la table jointure de Probleme et de Contact (avec une rubrique commentaire en plus des deux id qui font la liaison dans la table)


    Car d'un autre côté, quelqu'un m'a dit de créer un tableau de MContact (et non pas MProbleme_Contact car apparemment ça ne se fait pas de travailler sur les tables jointures) dans ma classe MProbleme. De faire une requête pour récupérer les Contact ID de ma table Probleme_Contact pour remplir ce tableau de MContact. Et ensuite de parcourir chaque Contact de mon tableau, d'allouer un objet Societe en passant l'id société du contact et de charger l'objet société (Donc un Hlitrecherchepremier sur la table société), puis ensuite d'allouer un objet MProbleme_Contact en passant l'id probleme et l'id contact et de charger l'objet (autre hlitrecherchepremier sur table Probleme_Contact) pour récupérer le commentaire présent dans Probleme_Contact.
    Le problème de cette solution, qui apparemment se fait de cette façon en JAVA pour la POO, c'est que je dois exécuter une première requête pour récupérer mes contacts lié à un problème, et ensuite parcourir mon tableau de MContact et faire deux HlitRecherchePremier (Donc deux autres actions/requêtes sur le serveur) pour récupérer tout ce dont j'ai besoin. C'est opti de faire ça ? J'avais lu quelque part qu'il fallait justement éviter de faire plusieurs hlitrecherchepremier dans une boucle de parcourt mais je me trompe peut-être.



    Attention, ajouter ou supprimer une ligne dans ta variable n'influe pas sur le contenu de ton fichier. Tu t'exposes, alors, à ne plus avoir ton tableau de visualisation en phase avec le contenu de ton fichier en base.
    Oui je me doute bien, c'était dans le cas où j'ouvre une fiche pour ajouter un élément, que je l'enregistre en BDD et qu'à la sortie, j'ajoute simplement cet élément à mon tableau mémoire pour éviter de tout rafraîchir. C'est surtout utile dans le cas où j'ouvre une fiche pour ajouter un élément qui ne doit pas être directement enregistré en BDD mais une fois que l'utilisateur valide tout (donc je ne peux pas rafraîchir toute ma table, je dois ajouter mes lignes à mon tableau et ensuite parcourir ce tableau pour justement enregistrer en BDD). Exemple : Je crée une commande, j'ai une table produits, donc j'ajoute mes produits en mémoire (Tableau) avant de tout enregistrer à la validation via le parcourt de ce tableau.


    C'est, je crois ce que je te propose. Cependant, tu n'es pas obligé de créer une "classe tableau" associée pour toutes tes classes mappées si tu n'en as pas besoin dans ton application
    Je pense que j'en aurais besoin pour beaucoup de classes, vu que même pour remplir un combo avec des éléments qui proviennent d'une table de mon analyse, je dois créer un tableau et databindé le tableau sur mon combo , y'a peut-être moyen de créer une classe générique de tableau pour éviter de retaper à chaque classe tableau de classes les méthodes "AjoutElementAuTableau" "SupprimeElementDuTableau" "ModifieElementDuTableau" et "ChargeTableau"

    A mon sens, il te faut une fenêtre pour ajouter un contact et une pour supprimer un contact. Une fois une de ces actions faite, tu peux recharger ta visu tableau à partir de ta requête et ta visu sera alors en phase avec le contenu de ton fichier.
    Ces vues manipulent des MProbleme_contact en CRUD.
    Oui c'est ce que je comptais faire pour la plupart des cas, j'ai déjà mes fenêtres pour ajouter/modifier un élément avec ajout/modif en bdd à la validation. Puis je recharge pour avoir les données à jour après validation.


    Merci


    Esteban

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 140
    Points : 131
    Points
    131
    Par défaut
    bonjour
    je trouve votre démarche vraiment intéressante. Seriez-vous prêt de mettre à dispo votre projet exemple.
    cela permettrait de mieux comprendre ce genre de concept via windev

  7. #7
    Membre actif

    Homme Profil pro
    Sans
    Inscrit en
    Mars 2018
    Messages
    153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Sans

    Informations forums :
    Inscription : Mars 2018
    Messages : 153
    Points : 295
    Points
    295
    Billets dans le blog
    1
    Par défaut
    Salut Djsven,

    Je le répète, il n'est pas opportun de travestir tes classes MMapping_de_fichier en y incluant des membres référençant des objets d'une autre classe.

    Dans le cas qui t'occupe, une simple requête est suffisante pour récupérer les infos dont tu as besoin.

    Je te propose, dans un premier temps, d'externaliser le code de ta requête dans un fichier requête REQ_ChargerProblemeContact à mettre, par exemple dans le dossier "Requêtes" de ton projet.

    Puis dans les déclarations globales de ta vue où se trouve ta table de visu (TAB_VisuProblemeContact ?) tu saisis le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    sourceDeDonnees est une Source de Données = REQ_ChargerProblemeContact 
     
    sourceDeDonnees.ParamPBcode = nProblemeCode
     
    SI PAS HExécuteRequête(MaRequete,hRequêteDéfaut) ALORS 
    	erreur(HErreurInfo)
    	RENVOYER Faux
    SINON
    	FichierVersTableMémoire(TAB_VisuProblemeContact , sourceDeDonnees)  // Ici, il faut que les colonnes de ta table correspondent en nombre et en ordre au champs sélectionnés dans ta requête
    FIN
    Ceci devrait déjà te permettre de visualiser un résultat.

    Par contre, on mélange allègrement code métier et IHM.

    Je te propose, ensuite, de créer une classe "cModeleDeTousMesProblemes" dans laquelle tu implémentes une méthode "getMesProblemesParCode" avec le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    PROCEDURE getMesProblemesParCode(pProblemeCode est une chaine) : source de données
    sourceDeDonnees est une Source de Données = REQ_ChargerProblemeContact 
     
    sourceDeDonnees.ParamPBcode = pProblemeCode 
     
    SI PAS HExécuteRequête(MaRequete,hRequêteDéfaut) ALORS 
    	erreur(HErreurInfo)
    	RENVOYER Faux
    SINON
    	RENVOYER sourceDeDonnees
    FIN
    A partir de maintenant, pour avoir la liste de tes problèmes, il te suffit d'intancier un objet de type cModeleDeTousMesProblemes et de lui demander la liste :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    oMesProblèmes est un cModeleDeTousMesProblemes dynamique <- allouer un cModeleDeTousMesProblemes
    FichierVersTableMémoire(TAB_VisuProblemeContact , oMesProblèmes.getMesProblemesParCode(leCodeEnParametre))
    Si tu donnes une visibilité GLOBALE à ta méthode, tu peux même l'utiliser de cette manière :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    FichierVersTableMémoire(TAB_VisuProblemeContact , cModeleDeTousMesProblemes.getMesProblemesParCode(leCodeEnParametre))
    Enfin, afin d'utiliser le pattern MVP, tu peux associer une classe de présentation à ta fenêtre qui implémentera une méthode de récupération de la source de données :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    PROCEDURE getMesProblemes(avec ou sans parametres je ne sais pas ou tu le prends)
     
    RENVOYER cModeleDeTousMesProblemes.getMesProblemesParCode(leCodeEnParametre)
     
    // Ou encore
    // oMesProblèmes est un cModeleDeTousMesProblemes dynamique <- allouer un cModeleDeTousMesProblemes
    // RENVOYER oMesProblèmes.getMesProblemesParCode(leCodeEnParametre)
    Et pour répondre à ceci :
    ...
    Car d'un autre côté, quelqu'un m'a dit de créer un tableau de MContact (et non pas MProbleme_Contact car apparemment ça ne se fait pas de travailler sur les tables jointures) dans ma classe MProbleme. De faire une requête pour récupérer les Contact ID de ma table Probleme_Contact pour remplir ce tableau de MContact. Et ensuite de parcourir chaque Contact de mon tableau, d'allouer un objet Societe en passant l'id société du contact et de charger l'objet société (Donc un Hlitrecherchepremier sur la table société), puis ensuite d'allouer un objet MProbleme_Contact en passant l'id probleme et l'id contact et de charger l'objet (autre hlitrecherchepremier sur table Probleme_Contact) pour récupérer le commentaire présent dans Probleme_Contact.
    Le problème de cette solution, qui apparemment se fait de cette façon en JAVA pour la POO, c'est que je dois exécuter une première requête pour récupérer mes contacts lié à un problème, et ensuite parcourir mon tableau de MContact et faire deux HlitRecherchePremier (Donc deux autres actions/requêtes sur le serveur) pour récupérer tout ce dont j'ai besoin. C'est opti de faire ça ? J'avais lu quelque part qu'il fallait justement éviter de faire plusieurs hlitrecherchepremier dans une boucle de parcourt mais je me trompe peut-être.
    ...
    Ce n'est certainement pas l'idéal en terme de performance et tu l'as bien senti. En JAVA, on peux faire du bon et du moins bon comme avec tous les langages...

    PS : J'ai tapé le code à la volé dans le navigateur. Des corrections et ajustements sont sans doute nécessaires

  8. #8
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2020
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2020
    Messages : 66
    Points : 35
    Points
    35
    Par défaut
    Salut Padbrain, merci pour ta réponse et pour la méthode, juste quelques dernières petites questions :

    La classe "cModeleDeTousMesProblemes" est une classe spécifique et propre pour cette requête ? ou si par exemple j'avais d'autre tableau à remplir dans la même fenêtre j'utiliserais cette même classe avec d'autres méthodes et d'autres requêtes ?

    Je demande car, bien que ta méthode fonctionne pour remplir la table, il faudrait que je récupère les données de la source (Req) dans un tableau d'objets (de cette requête) afin de faire du databinding sur les colonnes de ma table visuelle.
    Dans 90% des cas vu que je vais rafraichir tout lors de l'ajout/modif d'un élément, ta méthode est bonne et pas besoin de databinding sur la table, juste à rappeler la méthode pour reremplir la table. Cependant dans cet exemple présent, quand je vais ajouter/modifier un contact, une fenêtre s'ouvre pour l'ajout/modification, mais quand je valide, je n'enregistre pas directement en bdd, mais uniquement lors de la validation complète de la fenêtre du prob. De ce fait, en cas de modif, je dois passer la ligne sélectionné (l'objet) en paramètre, par référence, dans la fenêtre d'ajout/modif afin qu'elle se mette à jour dans ma table visuelle lors de la validation. De même en ajout, je dois instancier un objet de mon tableau d'objet databindé sur ma table visuelle, afin de récupérer en sortie les données saisies dans la fenêtre d'ajout, pour l'ajouter ensuite à mon tableau d'objet + demandeMiseAJourUI("Contact") pour rafraichir la table par rapport au tableau d'objet databindé. Je sais pas si tu vois ce que je veux dire. J'utilise pas mal la fonction demandeMiseAJourUI(+mon paramètre) afin de centraliser mes "tableaffiche()" (qui remettent à jour mes tables visuelles par rapport à leur tableaux d'objets databindé) dans l'évènement demande de mise à jour de l'affichage de ma fenêtre.

    De ce fait, il faudrait probablement que j'ajoute les rubriques de ma requête comme membre de la classe que tu m'as dit de créer si cela se fait. Comme ça, quand je fais le "getMesProblemesParCode", je déclare un tableau de cette même classe que je remplie via ma source de données par des fichiersversmémoire ou tableauversmemoire, et je renvoie mon tableau d'objet que je peux ensuite récupérer à l'appel de cette méthode et le databinder sur ma table visuelle (plutôt que passer par le FichierVersTableMémoire). Ou alors je créé simplement une classe avec les membres de ma requête, et une autre classe qui est un tableau de cette classe requête avec les méthodes charge, ajout, modif, suppression en mémoire comme t'avais proposé plus haut à part que là ça serait pour une classe en gros "mappé" sur ma requête ?


    Ensuite, en ce qui concerne la validation complète de la fenêtre, je dois parcourir ma table visuelle afin de faire les ajouts/modif en bdd, dans ce cas pour chaque ligne (ou chaque objet de ma classe requête) de ma table, j'alloue un objet de MProbleme_Contact, je remplie les membres manuellement par rapport à mes colonnes qui m'intéressent et je fais un "MonObjet.BEnregistre" (méthode pour enregistrer en bdd), c'est bien ça ?


    Merci

  9. #9
    Membre éclairé
    Homme Profil pro
    Chef de projet
    Inscrit en
    Mars 2017
    Messages
    336
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2017
    Messages : 336
    Points : 809
    Points
    809
    Par défaut
    C'est du MVP, vu que la vue n'a pas accès au modèle. Certes, une couche de plus s'occupe de la présentation puisque tu as les classes mappées + les classes de présentation propres aux fenêtres, ce qui n'est pas forcément plus mal en soi. Je ne m'en sert pas systématiquement car je veux éviter que la vue parle au modèle, mais la vue n'a pas pour autant besoin de sa classe, elle utilisera celle qui est mappée sur le fichier (mais la vue ne parle pas au modèle, seule la classe le peut).
    Pour les projets que je mène cela suffit et permet un compromis dans les coûts et les délais. Il serait certes mieux d'utiliser les classes de présentation toutefois, mais ça n'a jamais été un frein dans mes projets.

    Maintenant que je vois ton analyse, attention aux types de fichiers: perso tous les fichiers de mes analyses sont des HF Classic. Ensuite par code, je gères les changement de connexion grâce à une classe dont c'est le boulot. Plus facile pour les tests.

    1) Ca m'a l'air plutôt pas mal
    2) Ce tableau stocke les contacts liés au problème, il n'a donc pas une vocation premièrement visuelle, au niveau métier c'est effectivement logique qu'il soit là. La vue pourrait s'en servir si elle voulait mais autrement, il ne me semble pas à la mauvaise place. Par contre, il ne sera peut être pas utile de le charger systématiquement.

    3)Le Societe_Probleme est l'entité de base, tout part du problème, donc dans la classe MProbleme il doit effectivement y avoir une méthode permettant de remplir le tableau m_tabProblemeContact. C'est MProbleme qui possède ce tableau, c'est donc à elle de fournir le moyen d'y accéder, mais ce moyen peut être l'appel à une méthode de la classe MProbleme_Contact. Car c'est cette dernière qui en fait gère les contacts associés a un problème. Donc MProbleme passe le relais à la classe MProbleme_Contact et lui demande de lui envoyer le tableau correspondant au problème qu'elle représente. Je procède de plusieurs façons selon le cas mais j'utilise souvent des property dans la classe, l'accès à la property peut provoquer plusieurs comportements:
    -le tableau de la classe est vide, et l'accès à la property déclenche son remplissage en appelant la méthode de la classe MProbleme_Contact (donc émet une requête ou plusieurs en base)
    -le tableau n'est pas vide, donc je renvoie le tableau en mémoire dans l'objet (utile si pas forcément de besoin de maj temps réel, évites de requêter la base pour rien)
    -le tableau n'est pas vide, mais je force systématiquement le reload: la property vide le tableau et le recharge puis le renvoie

    Il faut faire attention dans tout ça à ce que le tableau soit privé dans la classe et accessible via property uniquement. La property renseigne le tableau de la classe et le renvoie mais de l'extérieur on ne pourra pas y accéder. Mais on peut le laisser en public ça dépend du cas. Il faut en tout cas prévoir de pouvoir remplir ce tableau, de forcer ou non son actualisation, et faire attention car cela renvoie un tableau contenant des objets MProbleme_Contact, qui contiennent eux même un objet m_pclContact...et lui, qui le met à jour? Car il provient d'une table différente de l'analyse. Il faut donc aussi gérer cette cascade.

    4)Non, pas dans la présentation. Cette méthode va agir sur l'objet et va donc provoquer des modifs de la base potentielles. C'est un traitement métier à mettre dans la classe mappée à la base. Par exemple, tu pourrais avoir une liste de contacts numérotés, si tu ajoutes un contact, quelqu'un devra bien s'occuper de la renumérotation, idem si tu supprimes. Ce sont des traitements métiers que la classe de présentation propre à la fenêtre ne doit pas assumer, la classe de présentation reste une partie de la vue, et pas du contrôleur si je puis l'exprimer ainsi. A la place, la classe présentation va appeler sa collègue MProbleme qui possède les données et lui dire via une méthode de cette classe mappée "ajoutes moi ce contact". C'est ensuite la classe mappée qui va l'ajouter dans le tableau en mémoire et faire les contrôles métier nécessaires. Toujours penser à ce que deviendrait le traitement d'ajout si la fenêtre était supprimée: on peut plus ajouter du coup? Pourtant la classe qui est mappée au fichier des problemes existe toujours, même si la vue à disparue. La vue est bête, elle ne fait rien d'autres qu'afficher l'info et demander des choses à l'étage d'en dessous.
    Dans ce cas je pense que MProbleme doit fournir une méthode _ajouterContactAuProbleme qui va en fait ajouter un objet dans le tableau m_tabProblemeContact. Ensuite, MProbleme fournira une méthode de sauvegarde de ce tableau, qui elle même pourra appeler pour chaque objet de ce tableau la méthode de sauvegarde de la classe MProbleme_Contact. Mais avec le lot de requêtes qui vont avec évidemment. La difficulté ici est que m_tabProblemeContact est un tableau de MProbleme_Contact est un tableau d'objets, il ne représente pas la classe elle même. Il contient des objets de la classe, ce n'est pas tout à fait pareil.

    Dans la philosophie objet pure, il faudrait une classe MProbleme_Contact et une classe MTableauDeMProbleme_Contact. Ainsi au lieu d'avoir un tableau stockant des objets, on aurait un objet stockant des objets, et du coup le premier objet disposerait de méthodes propres pour sauver ses objets MProbleme_Contact: c'est ta dernière solution, mais c'est pas la plus rapide à implémenter bien sur, même si je trouve que c'est la plus propre!

  10. #10
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2020
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Aisne (Picardie)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2020
    Messages : 66
    Points : 35
    Points
    35
    Par défaut
    Salut kunnskap, merci pour la réponse, je reviens juste par rapport à ta réponse sur le (3)

    Maintenant que je vois ton analyse, attention aux types de fichiers: perso tous les fichiers de mes analyses sont des HF Classic. Ensuite par code, je gères les changement de connexion grâce à une classe dont c'est le boulot. Plus facile pour les tests.
    Ça marche, j'utilisais mon PC comme serveur pour simuler le HF C/S sur une base multi dossier, d'où mes tables en HF C/S


    Citation Envoyé par kunnskap Voir le message
    3)Le Societe_Probleme est l'entité de base, tout part du problème, donc dans la classe MProbleme il doit effectivement y avoir une méthode permettant de remplir le tableau m_tabProblemeContact. C'est MProbleme qui possède ce tableau, c'est donc à elle de fournir le moyen d'y accéder, mais ce moyen peut être l'appel à une méthode de la classe MProbleme_Contact. Car c'est cette dernière qui en fait gère les contacts associés a un problème. Donc MProbleme passe le relais à la classe MProbleme_Contact et lui demande de lui envoyer le tableau correspondant au problème qu'elle représente.
    Nom : 2020-02-12.png
Affichages : 3151
Taille : 14,8 Ko

    1°) En faite, tout part du problème si on est dans le sens du problème. Mais si on est dans le sens du contact et que je souhaite récupérer cette fois-ci les problèmes dont un contact a été associé. Je dois également ajouté côté classe MContact un tableau m_tabContactProbleme qui est également un tableau de MProbleme_Contact (classe de ma table jointure). Sauf que dans ce cas, pour charger ce tableau, la méthode tabCharge de la classe MProbleme_Contact ne fonctionne plus puisque je suis du côté contact (donc la requête est différente, et le paramètre de filtre aussi du coup, je ne filtre plus par rapport à un problème mais par rapport à un contact). Dans ce cas, dois-je ajouter une seconde méthode tabCharge dans ma classe MProbleme_Contact afin de renvoyer cette fois-ci un tableau de problèmes pour un contact donné ? Et ajouter un autre sous objet dans MProbleme_Contact (m_pclProbleme est un MProbleme dynamique en plus du sous objet m_pclContact est un MContact dynamique pour faire correspondre cette fois-ci les rubriques de ma requête aux membres du sous objet MProbleme via fichierversmemoire récursif) ?

    En faite, c'est la rubrique commentaire présent dans cette table jointure qui me pose problème, car du coup je suis obligé de déclarer des tableaux d'une classe mappé sur une table jointure MProbleme_Contact dans ma classe MProbleme et dans ma classe MContact (et je sais même pas si de base on a le droit).



    2°) Car dans un second cas, si par exemple (j'ai des cas de ce style dans mon projet), je n'aurais que les deux identifiants uniques dans ma table jointure, sans le commentaire, j'aurais donc une table jointure classique sans autres rubriques :

    Nom : 2020-02-12_10h25_11.png
Affichages : 3128
Taille : 12,0 Ko

    De ce fait, dans ce cas, je suppose que j'aurais simplement à déclarer dans ma classe MProbleme : m_tabContact est un tableau de MContact OU m_pclContact est un MTableauDeContact (si j'utilise la méthode classe de tableau d'objet possédant les méthodes de chargement, etc) et dans ma classe MContact : m_tabProbleme est un tableau de MProbleme OU m_pclProbleme est un MTableauDeProbleme, non ?

    Puis ensuite créer une méthode dans MContact _ChargeContactDunProbleme(pCodeProbleme) pour me renvoyer un tableau de contact pour un problème donné et appeler cette méthode dans MProbleme en y passant le code probleme en paramètre pour charger m_tabContact ?
    Même principe si j'utilise une classe de tableau d'objets. Ex MTableauDeContact, dedans j'aurais une méthode de base pour charger tout mes contacts, et j'aurais besoin de créer une nouvelle méthode pour charger tout mes contacts pour un problème donné. Dans ma classe MProbleme, j'aurais m_pclContact est un MTableauDeContact et une méthode qui va appeler la méthode de MTableauDeContact pour charger cet objet en passant le code problème en paramètre à cette méthode.

    (C'est bien dans la classe MContact que je dois créer une méthode et exécuter la requête sur la table jointure Probleme_Contact avec en paramètre de la req le code problème reçu dans la méthode, pour renvoyer le tableau de contacts d'un pb?)

    Je suppose que c'est ce principe là quand il n'y a pas d'autres rubriques (autres que les deux id unique) dans une table de jointure, et donc pas besoin de déclarer des tableaux d'objets des classes modèles de tables jointures, contrairement à quand il y a d'autres rubriques dans ces tables

    Je procède de plusieurs façons selon le cas mais j'utilise souvent des property dans la classe, l'accès à la property peut provoquer plusieurs comportements:
    -le tableau de la classe est vide, et l'accès à la property déclenche son remplissage en appelant la méthode de la classe MProbleme_Contact (donc émet une requête ou plusieurs en base)
    -le tableau n'est pas vide, donc je renvoie le tableau en mémoire dans l'objet (utile si pas forcément de besoin de maj temps réel, évites de requêter la base pour rien)
    -le tableau n'est pas vide, mais je force systématiquement le reload: la property vide le tableau et le recharge puis le renvoie

    Il faut faire attention dans tout ça à ce que le tableau soit privé dans la classe et accessible via property uniquement. La property renseigne le tableau de la classe et le renvoie mais de l'extérieur on ne pourra pas y accéder. Mais on peut le laisser en public ça dépend du cas. Il faut en tout cas prévoir de pouvoir remplir ce tableau, de forcer ou non son actualisation, et faire attention car cela renvoie un tableau contenant des objets MProbleme_Contact, qui contiennent eux même un objet m_pclContact...et lui, qui le met à jour? Car il provient d'une table différente de l'analyse. Il faut donc aussi gérer cette cascade.
    J'ai besoin justement de faire du databinding sur mes tables visuelles à partir des tableaux mémoires. Le tableau de ProblemeContact dans MProbleme sera databindé sur ma table visuelle présent dans ma fiche Problème. Si je rend ces tableaux privés, je ne peux plus faire le databinding sur ces tableaux

    Pour m_pclContact, je le mettais à jour en même temps que l'objet MProbleme_Contact vu que j'utilise une requête qui va me récupérer le code contact, le commentaire (table jointure), et le nom prénom du contact (table contact), donc quand j'exécute la requête je fais des fichierversmemoire récursifs sur chaque objet/sous objet (ta fonction que tu m'avais montré sur l'autre topic)


    Bonne journée


    PS : Par rapport à padbrain qui je cite :
    Citation Envoyé par padbrain
    Il n'est pas opportun de travestir tes classes MMapping_de_fichier en y incluant des membres référençant des objets d'une autre classe.
    Toi tu n'y vois donc pas d'inconvénients ? Je ne dis pas qu'une méthode est meilleure que l'autre, simple question. Car il y a visiblement pas mal de façon de faire pour un pattern et une architecture choisie

  11. #11
    Membre éclairé
    Homme Profil pro
    Chef de projet
    Inscrit en
    Mars 2017
    Messages
    336
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Chef de projet
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2017
    Messages : 336
    Points : 809
    Points
    809
    Par défaut
    Astuce pour le CS: l'utiliser sur un serveur distant, style un VPS OVH qui suffira pour les tests. Tu te rendras compte des endroits ou ton appli rame à fond car les appels au serveur ne sont pas optimisés. Alors qu'en local, tu ne verras rien, et le jour ou tu voudras mettre l'appli en distant, t'auras des surprises (et les décideurs aussi).

    Oui, fatalement, tu peux y aller dans tous les sens avec ce genre de cas. Si tu n'as pas besoin de toutes les méthodes, pas la peine de les créer, tu le feras au besoin. Dans mon cas, je n'avais pas à le faire, car la méthode que je t'ai passée (et surtout, toute la mise en oeuvre qu'il y avait autour), permettait de simplement déclarer les objets/tableau d'objet dans les classes, et l'ORM faisait toutes les requêtes derrière pour tout remplir peut importe le sens que tu prenais. Evidemment le temps de conception était beaucoup plus long mais ça marchait très bien et je ne me souciais pas de méthodes manquantes.
    Car en fait, tu devrais voir ça de manière plus large. Dans ce cas précis tu vas en effet requêter des tables différentes sur des ID différents, mais plus largement, qu'est ce qui va changer quand tu vas requêter ta base pour obtenir des infos issues de tables de jointure (et pas que ça d'ailleurs...)?
    C'est en le codant à la main comme toi que je me suis rendu compte tout seul que j'abusais du copier coller pour au final ne changer que quelques rubriques, et je me suis dit "y'a une put**** de factorisation à faire" et je l'ai faite. Sinon oui, tu vas devoir ajouter des méthodes à la main, et leur code sera semblable.


    Pour le champ commentaire oui, il est un peu chiant placé la comme ça je n'avais pas de champ en plus des clés. Et dans la classe qui représente la table de jointure j'avais 2 objets, puisque les 2 ID correspondent à 2 autres tables. Mais mon ORM faisait que lorsque je chargeais un enregistrement de la table de jointure dans son objet, il faisait tout seul les JOIN pour aller chercher les objets liés. Ce qui semble te gêner c'est d'avoir une représentation objet de la table de jointure au milieu de tes classes, je l'ai gommée en utilisant dans le code cette instance de classe mais en accédant à ses sous-objets; ainsi je ne voyais même plus la table de jointure en fait. Elle n'était là que pour....joindre. C'est ce que tu dis avoir fait avant ton bonne journée, et c'est très bien car tu n'auras pas à t'en soucier dans le code.

    Plus largement, tu dois sectionner les responsabilités: une classe ne peut pas être responsable de tout. Quand une classe MClient possèdes des MFactures, ce n'est pas à MClient d'implémenter la méthode de sauvegarde d'une facture par exemple. Chaque classe gère un périmètre, à elle de le gérer bien et de ne pas en sortir. C'est l'une des dérives possibles de l'objet, enfermer dans une classe des traitements qu'elle ne devrait pas avoir à faire.

    Effectivement si tableau privé pas de databinding, mais les attributs privé/protégé/public sont à adapter au cas par cas, je n'ai pas assez d'élements dans le projet pour dire si ils doivent être public ou privés. J'ai tendance à rendre privé un peu beaucoup de choses dans les objets, car si on laisse tout public, c'est comme si on fait que du GLOBAL dans les méthodes: l'objet n'a aucun intérêt.

    Sur le travestissement des classes dont parle padbrain, sur le fond il n’a pas tort. Le fait est qu’en ajoutant une référence à une classe X dans une classe Y, tu crées une dépendance. Par l’injection de dépendance, c’est améliorable (par exemple voir http://igm.univ-mlv.fr/~dr/XPOSE2010...sentation.html)
    Je ne le fais pas, car ça allongerais les temps de conception, et on est souvent limite dans les environnements où je bosse concernant les coûts et délais, il faut donc trouver des compromis et s’adapter. Le compromis que je t’explique m’a jusque là permis de gérer des applis pour des milliers de client en assurant une maintenabilité et une testabilité tout à fait correcte. Gérer l'injection de dépendance, c'est une grosse gourmandise dont je dois malheureusement me passer. Et mettre le temps ailleurs dans d'autres choses. Par exemple en ce moment je fais de l'API REST, je n'ai pas d'injection de dépendance mais j'ai passé du temps sur la consolidation des logs...car oui, de l'injection de dépendance dans le code ça serait fun mais ça ne servira pas à grand chose au client, vu le délai dans lequel je dois mettre en prod. Par contre, si un client fait une connerie avec les API, je serais bien content d'avoir blindé les logs pour le prouver!

Discussions similaires

  1. POO : Création d'un système question/réponses
    Par Kenshin_Himura dans le forum Langage
    Réponses: 0
    Dernier message: 04/05/2010, 00h49
  2. Réponses: 0
    Dernier message: 24/09/2008, 18h03
  3. [Architecture] Question d'architecture
    Par bourbaki2003 dans le forum Général Java
    Réponses: 3
    Dernier message: 11/07/2006, 11h38
  4. [Architecture] Questions DB, Arch, Tech pour un project
    Par Ultiny dans le forum Développement Web en Java
    Réponses: 3
    Dernier message: 02/05/2006, 16h04
  5. [Architecture] Question data layer et présentation
    Par brousaille dans le forum Plateformes (Java EE, Jakarta EE, Spring) et Serveurs
    Réponses: 16
    Dernier message: 14/01/2006, 13h48

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