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

Discussion :

Faire interagir deux objets

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut Faire interagir deux objets
    Bonjour

    Je viens ici car j'ai un soucis de conception. J'ai en effet 2 objets A et B indépendants mais j'ai à un certain moment besoin de faire exécuter une action par l'objet B quand un certain évènement survient à l'objet A.

    J'ai donc 2 possibilités
    1. je peux tout d'abord créer une méthode "action()" dans l'objet B et l'appeler depuis l'objet A quand l'évènement arrive. C'est de la programmation objet classique et tout à fait faisable puisque les deux objets se connaissent.
    2. je peux aussi connecter dans l'objet B un SLOT qui va lancer l'action quand l'objet A émet un certain signal. Ensuite, quand l'objet A reçoit l'évènement, il n'a qu'à émettre ce signal


    J'ai testé les 2 façons de faire et ça fonctionne dans tous les cas. Maintenant est-ce que certains auraient des conseils sur le choix d'une ou de l'autre ? Le mécanisme signal/slot est tentant mais il a été conçu au départ pour offrir une certaine indépendance. Un signal peut être connecté à plusieurs slots distincts et un même slot peut être activé par plusieurs signaux distincts. Or ici ce n'est pas le cas car l'évènement de l'objet A ne doit faire intéragir que B et seul cet évènement de A doit produire l'action de B. Donc en fait dans mon esprit je me dis que je fais appel à un bien beau mécanisme pour bien peu de chose.

    Donc voilà mon dilemne.

    Merci de votre attention.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  2. #2
    Membre éprouvé
    Avatar de ymoreau
    Homme Profil pro
    Ingénieur étude et développement
    Inscrit en
    Septembre 2005
    Messages
    1 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur étude et développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 1 154
    Par défaut
    Comme tu dis, le principe du signal slot permet une grande modularité. On ne s'occupe pas de qui fera quoi avec notre signal. Si dans ton cas il n'y a que peu de chance que l'évènement dans A ait un intérêt pour le reste de l'application, autant appeler directement la fonction de B. Le seul inconvénient est que ça lie B à A, mais si c'est déjà le cas au niveau de la logique entre les deux classes alors c'est plus simple d'appeler directement la fonction selon moi (et ça évite de polluer les interfaces des deux classes avec signal et slot).

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    De manière générale, la solution qui consiste à créer un signal au niveau de l'élément "qui doit provoquer la réaction" est sans doute la solution la meilleure, et ce, pour une raison bien simple : autrement, il devra en connaitre "suffisamment" sur l'objet qui doit réagir que... pour pouvoir appeler la fonction de réaction.

    Par contre, l'idéal est sans doute que ce ne soit pas l'objet qui doit réagir qui s'occupe de se connecter "tout seul, comme un grand" à ce signal. L'idéal serait que ce soit l'élément qui "regroupe les deux" qui s'en occupe, et ce, pour la raison inverse : l'élément réactif devrait en connaitre "suffisamment sur l'émetteur" que pour pouvoir se connecter au signal émis.

    Allez, prenons un petit exemple "tout simple" et considérons que l'on ne crée ni signal ni slot.

    Nous partons de la classe Emetteur (qui émet un signal quand "quelque chose survient) et la classe Recepteur (qui est sensé réagir à l'événement survenu auprès de Emetteur). Elles ressembleront sans doute à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Emetteur{
    public:
        Emetteur(QWidget * parent);
    SIGNALS:
        void monSignal();
    };
    class Recepteur{
    public:
        Recepteur(QWidget * parent);
    public SLOTS:
        void reaction();
    };
    Si tu voulais que l'émetteur connaisse le récepteur, tu serais obligé de modifier ta classe Emetteur pour lui donner la forme de
    Emetter.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* il faut au minimum la déclaration anticipée de Recepteur ici */
    class Recepteur;
    class Emetteur{
    public:
        Emetteur(QWidget * , Recepteur & r);
        /* ... */
    private:
        void onSuchEvent();
        Recepteur & r;
    };
    Et, dans le fichier d'implémentation d'Emetteur, tu devrais inclure le fichier d'en-tête de Recepteur pour que onSuchEvent() puisse effectivement appeler la "fonction qui va bien", sous une forme proche de
    Emetteur.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include <Recepteur.hpp>
    Emetteur::Emetteur(QWidget * parent, Recepteur & r):/* n'oublions pas la classe de base de Recepteur ici */r(r){}
    void Emetteur::onSuchEvent(){
        /* il nous fallait le fichier d'en-tête de Recepteur 
         * pour pouvoir appeler Recepteur::reactToEvent()
         */
        r.reactToEvent(/* *this ??? */);
    }
    Et il est même fort possible que tu aies besoin du fichier d'en-tête de Emetteur dans le fichier d'implémentation de Recepteur, afin de pouvoir récupérer certaines données de Emetteur dans la fonction reactToEvent() !!!

    Pas cool : tu viens de créer une dépendance circulaire : sans le fichier Recepteur.hpp, ta classe Emetteur ne peut pas fonctionner, et sans le fichier Emetteur.hpp, ta classe Recepteur ne peut pas fonctionner non plus!

    Maintenant, voyons ce qui se passe avec un signal du coté de Emetteur et un slot du coté de Recepteur. Les classes prennent donc une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Emetteur{
    public: 
        /* ... */
    SIGNALS :
       void monSignal(/* les données qui intéressent Recepteur */);
    };
    code Recepteur{
    public:
        /* ... */
    public SLOTS:
        void reactionAuSignal(/* les données qui intéresent Recepteur */);
    };
    (tu auras compris que "les données qui intéressent Recepteur correspondent au paramètres à transmettre, et que c'est à chaque fois la même liste de paramètres )

    Par contre, si tu crée un signal dans Emetteur et un slot dans Recepteur, les choses sont beaucoup plus sympa !!! Car il faut te dire que l'élément qui regroupe Emetteur et Recepteur a déjà besoin de disposer aussi bien du fichier d'en-tête d'Emetteur que du fichier de Recepteur.

    En effet, cet élément prend sans doute la forme de
    MyWindow.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class Emetteur;
    class Recepteur
    class MyWindow : public QMainWindow{
        /* ... */
    private:
        Emetteur * em;
        Recepteur * re;
    };
    et que, pour pouvoir créer les éléments pointés par em et par re, nous aurons de toutes facons besoin des fichiers d'en-tête de Emetteur et de Recepteur, sous une forme proche de

    MyWindow.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include <Recepteur.hpp>
    #include <Emetteur.hpp>
     
    MyWindow::MyWindow(/* */){
        /* on avait de toutes manière besoin des fichier d'en-tête ici */
       em = new Emetteur;
       re = new Recepteur;
    }
    Mais, du coup, le compilateur sait, quand il travaille sur MyWindow.cpp, que la classe Emetteur dispose d'un signal nommé monSignal, prenant un certain nombre de paramètres. Et il sait aussi que la classe Recepteur dispose d'un slot nommé reactionAuSignal et prenant... les même paramètres.

    Nous pouvons donc modifier le constructeur de notre class MyWindow pour qu'il connecte le signal de la classe Emetteur au slot de la classe Recepteur, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <Recepteur.hpp>
    #include <Emetteur.hpp>
     
    MyWindow::MyWindow(/* */){
        /* on avait de toutes manière besoin des fichier d'en-tête ici */
       em = new Emetteur;
       re = new Recepteur;
       connect(em, &Emetteur::monSignal, re, &Recepteur::reactionAuSignal);
    }
    Et le mieux de tout cela, c'est que nous avons connecter le signal au slot sans avoir à rajouter la moindre dépendance qui n'était pas encore nécessaire où que ce soit. Avoue que cela vaut la peine, non

    Mieux encore! Imaginons que, dans six mois, tu doive développeur une toute nouvelle fenêtre, composée d'un élément de type Recepteur, et d'une toute nouvelle classe qui devrait communiquer avec l'élément de type Recepteur et que cette classe ne soit pas de type Emetteur, mais qu'il soit malgré tout possible de trouver des valeurs des types adéquat pour émettre le signal.

    Il n'y a rien qui t'empêche de créer cette nouvelle classe, de définir le signal exactement comme celui que tu avais défini pour Emetteur, et de le connecter au slot de Recepteur. Et tu auras évité de développer un RecepteurDeCetteAutreClasse dont les comportements auraient été au final exactement les mêmes que ceux de Recepteur.

    Et, bien sur, cela va dans les deux sens : tu peux tout aussi bien réutiliser ta classe Emetteur et redéveloppeur une nouvelle classe pour réagir (différemment que la classe Recepteur) au signal émi par Recepteur. Encore une fois, divise le travail à faire par deux en n'ayant pas à créer une AutreClasseEmettantLeSignal
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ymoreau Voir le message
    Comme tu dis, le principe du signal slot permet une grande modularité. On ne s'occupe pas de qui fera quoi avec notre signal. Si dans ton cas il n'y a que peu de chance que l'évènement dans A ait un intérêt pour le reste de l'application, autant appeler directement la fonction de B. Le seul inconvénient est que ça lie B à A, mais si c'est déjà le cas au niveau de la logique entre les deux classes alors c'est plus simple d'appeler directement la fonction selon moi (et ça évite de polluer les interfaces des deux classes avec signal et slot).
    Bonjour

    Merci de ta réponse. Elle est très logique mais je t'avoue que j'aurais préféré l'autre style "tu devrais quand-même passer par des signaux/slots parce que ..." parce que je t'avoue que malgré ce que j'ai dit, elle me tente grandement et je ne sais pas pourquoi donc si tu m'avais donné une raison de la préférer ça aurait été super (mais c'est quand-même super sympa d'avoir pris le temps d'exposer ton point de vue )

    Citation Envoyé par koala01 Voir le message
    De manière générale, la solution qui consiste à créer un signal au niveau de l'élément "qui doit provoquer la réaction" est sans doute la solution la meilleure, et ce, pour une raison bien simple : autrement, il devra en connaitre "suffisamment" sur l'objet qui doit réagir que... pour pouvoir appeler la fonction de réaction.
    Oui, c'est logique. Il faut bien évidemment que B connaisse A pour pouvoir appeler A.action(). Ou alors A et B sont tous deux issus de C (et A et B connaissent leur parent C bien évidemment) ce qui permet alors à B d'appeler parent.action() ("parent" étant bien évidemment "C") et C qui appellera alors A.action(). Un peu alambiqué mais possible.

    Citation Envoyé par koala01 Voir le message
    Par contre, l'idéal est sans doute que ce ne soit pas l'objet qui doit réagir qui s'occupe de se connecter "tout seul, comme un grand" à ce signal. L'idéal serait que ce soit l'élément qui "regroupe les deux" qui s'en occupe, et ce, pour la raison inverse : l'élément réactif devrait en connaitre "suffisamment sur l'émetteur" que pour pouvoir se connecter au signal émis.
    Exact. Mais je n'ai pas dit que B se connectait à A
    C'est effectivement déjà le cas. C appelle A et B et C connecte lui-même B à A.

    Citation Envoyé par koala01 Voir le message
    Allez, prenons un petit exemple "tout simple" et considérons que l'on ne crée ni signal ni slot.

    […]
    Super exemple très didactique et très complet. Merci d'avoir pris le temps de l'écrire. Je regrette de ne pouvoir voter qu'une seule fois.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [POO] Faire communiquer deux membres d'un même objet
    Par emurb dans le forum Langages de programmation
    Réponses: 2
    Dernier message: 06/01/2013, 22h42
  2. [Liferay] Faire communiquer un objet entre deux portlets
    Par un-julien dans le forum Portails
    Réponses: 2
    Dernier message: 23/05/2011, 18h33
  3. faire varier une distance entre deux objets
    Par Neuropsy dans le forum VB.NET
    Réponses: 1
    Dernier message: 05/11/2009, 09h44
  4. Problème pour faire bouger deux objets simultanement
    Par LinuxUser dans le forum AWT/Swing
    Réponses: 13
    Dernier message: 22/04/2007, 23h19
  5. Afficher deux objets superposés C++ Builder 6
    Par nicoistia dans le forum C++Builder
    Réponses: 2
    Dernier message: 12/03/2004, 15h09

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