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

 C++ Discussion :

Classe Utilisateur hérité


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre prolifique
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    10 246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 10 246
    Par défaut Classe Utilisateur hérité
    Bonjour, je réalise un logiciel sous QDevelop en ce moment.
    J'ai une classe utilisateur, une classe operateur qui est une sorte d'utilisateur, et une classe administrateur qui est une sorte d'opérateur, je pense que si ce que j'ai dis est correct vous avez compris sans que j'ai besoin de montrer le diagramme de classe.

    Au début du logiciel l'utilisateur se connecte en entrant son login et son password, après on regarde dans la base de donnée si il existe une entrée avec ce login et ce password si c'est le cas il est connecté, dans la table utilisateur de la base de donnée, il y a également un champ "Rang" qui dit si il est Utilisateur, Operateur ou Administrateur.

    Je voudrais savoir comme créer une instance d'un des 3, comme ça va ce passer au niveau du code.
    Parce que là mon code craint, j'ai déclarer une instance d'utilisateur en extern...

    Par exemple dans le code de la fenetre principal il y a ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Utilisateur2.SetLogin(Login);
    Utilisateur2.SetPassword(Password);
    Pourriez-vous m'aider s'il vous plait ?

    ================================================================

    Edit : Mon professeur vient de me dire qu'au lieu d'avoir un champ Rang de type text qui donne le Rang, c'est mieux d'avoir des bools du genre EstAdministrateur et EstOperateur.

    Je vais essayer de faire un truc du genre si le rang est utilisateur => new utilisateur et si le rang est administrateur => new administrateur, pis après faire 3 if à chaque fois qu'avant j'avais Utilisateur2.

    ================================================================

    J'ai une classe mainwindow, une classe dialog, une classe mysql, etc...
    Exemple : dans mainwindow on recupere le login et le mot de passe, on envoie ça a la classe mysql, après selon le rang on créer un new utilisateur ou new operateur.

    J'ai besoin d'avoir l'utilisateur dans la classe dialog, comment je peux faire ?

  2. #2
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    507
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Mai 2006
    Messages : 507
    Par défaut
    Bonjour,

    Voici une idée, qui n'est sans doute pas la meilleure, mais qui est tout de même une idée.
    Dans ta classe mainwindow par exemple tu as une variable de classe de type "pointeur vers un objet utilisateur".
    En l'ayant dans cette classe, cela permettra de transmettre les infos aux autres classes qui en ont besoin.

    Pour l'initialiser, je pense que le mieux c'est que tu es une méthode du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Utilisateur* login (const QString& sLogin, const QString& sPwd);
    qui soit dans ta classe "Mysql". Avec le login et le mot de passe donné, cette méthode va vérifier sur dans ta db si l'utilisateur existe, et dans ce cas trouver à quelle catégorie il appartient. A ce moment, la fonction pourra faire un "new" sur la classe appropriée et retourner le pointeur. Comme les 3 classes sont des "Utilisateurs", cela devrait fonctionner... Ainsi, le retour de la fonction (appelée depuis la classe mainwindow) peut être conservé...

    Est-ce que je suis à peu prêt clair ?

  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,

    Je crois que tu devrais séparer clairement les choses dans l'ordre logique d'exécution:
    1- l'utilisateur introduit (dans l'IHM) son login et son mot de passe
    2- Quand il "clique" sur le bouton ad-hoc, l'IHM envoie les informations (login et mot de passe) à la partie métier (coté client), et plus précisément à la partie qui se charge de l'envoi des requêtes au serveur
    3- la partie métier (coté client) utilise les informations reçues pour créer une requête de connexion et l'envoie au serveur

    4- (coté serveur)le serveur reçoit la requète et renvoie un résultat:
    connexion refusée (login et / ou mot de passe incorrect)
    connexion réussie : type de privilèges (utilisateur / opérateur /administrateur / ...)

    5- (retour coté client ) la partie métier récupère le résultat de la requête et
    5.a- envoie un message à l'IHM "connexion refusée" si tel est le cas
    5.a.1 - l'IHM le fait savoir à l'utilisateur et "retour" en 1
    5.b- si la connexion est acceptée, la partie métier (coté client) crée:
    5.b.1- un utilisateur "simple" si le serveur a dit qu'il acceptait l'utilisateur simple
    5.b.2- un opérateur si le serveur a dit qu'il acceptait un opérateur
    5.b.3- un administrateur si le serveur a dit qu'il acceptait un opérateur
    5.c (uniquement si passé par 5.b) envoie un message "connexion acceptée" à lIHM
    5.c.1 l'IHM fait savoir à l'utilisateur qu'il est désormais connecté

    Nous avons donc déterminé qu'il y a quatre grands acteurs dans l'histoire:
    • L'IHM: c'est la partie "vue" du MVC(*)... C'est grace à elle que l'utilisateur peut interagir avec l'application.
    • La partie métier coté client: C'est un morceau de la partie Controleur du MVC(*)
    • La partie métier coté serveur: c'est un autre morceau de la partie Controleur du MVC(*)
    • Plusieurs type d'utilisateurs : ce sont quelques types issus de la partie "modèle" du VMC(*)

    (*)MVC: Model View Controller: pour faire simple, c'est une manière de travailler qui consiste à séparer les données de la manière dont elles sont manipulées par le système et de la manière dont elles sont présentées à l'utilisateur:

    La vue (la manière dont les données sont présentées à l'utilisateur) s'adresse au controleur (à la partie qui gère les données) pour obtenir des "bribes d'informations", et le contrôleur utilise le modèle pour répondre à la vue, après s'être assuré que la vue n'essaye pas de faire une bêtise avec les données.

    Dans le controleur, nous avons remarqué la présence de deux parties distinctes: ce qui se passe du coté du client et ce qui se passe du coté du serveur.

    Généralement, il y a réellement une séparation physique entre ces deux parties (l'une travaillant sur un ordinateur et l'autre se trouvant... à l'autre bout du câble réseau).

    Il faut donc... quelque chose qui assure la connexion entre les deux.

    Nous pouvons appeler cette partie du controleur le "gestionnaire de connexion" qui se contente (coté client) d'essaye de joindre le serveur, et de maintenir la connexion active une fois qu'il y est arrivé.

    L'HIM peut lui demander à n'importe quel moment "la connexion est-elle encore active"

    (il a un autre role coté serveur, mais je vais déjà écrire un roman assez long pour éviter d'en parler ici )

    Comme il y a des communications qui doivent passer entre le client et le serveur, il faut également un système qui "formate" ces communications et qui se trouve (toujours) dans la partie controller de MVC.

    Nous appellerons ce système le "gestionnaire de requêtes".

    Son rôle (coté client, car il a aussi une utilité coté serveur) consiste à recevoir les messages que peut émettre l'IHM et qui concernent... les demandes adressées au serveur, de créer des requêtes qui seront comprises par ce dernier, de les envoyer, et de récupérer les résultats qu'il transmet "à qui de droit".

    "Qui de droit", ca peut être le retour à l'ihm ("connexion refusée / acceptée"), le gestionnaire d'utilisateur (dont je parlerai plus tard) ou un système qui recrées les données au départ de la réponse obtenue.

    Il travaille pour se faire par... la connexion maintenue par le gestionnaire de connexion



    En effet, l'utilisateur peut avoir différents droits qui sont (ou non) accordés par le serveur.

    Il faut donc pouvoir "garder en mémoire" les droits accordés, et ca, c'est le role du gestionnaire d'utilisateur.

    Un utilisateur se résume à peu de chose du coté client: on peut récupérer son login (mais pas son mot de passe ), et le GO ou le STOP pour différentes actions envisageables ( l'utilisateur peut il obtenir un catalogue, introduire une nouvelle commande, demander un devis, ajouter un nouvel article, accéder à des commandes précédentes, auxquelles, accéder à l'en-cours d'un client, duquel, ...)

    L'IHM peut, éventuellement, s'adresser à ce gestionnaire afin d'activer / de désactiver certaines options.

    As tu compris tout cela Si ce n'est pas le cas, prend un café, allume une cigarette et recommence la lecture

    C'est bon, tu a compris ... Ok, si je t'explique tout cela, c'est pour te faire prendre conscience que ton IHM ne doit pas s'inquiéter de l'utilisateur réellement connecté: elle doit "simplement" s'adresser au gestionnaire d'utilisateur pour éventuellement savoir si il a le droit de faire telle ou telle action.

    Mais comme elle devra aussi accéder aux autres gestionnaire, il lui faut donc une variable (à laquelle tous les dialogues ont accès) qui permet d'accéder à chaque gestionnaire particulier.

    Elle sera sans doute fort proche de 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
    13
    class Metier
    {
        public:
            ConnectionManager & connexion(){return con_;}
            DataManager & data(){return data_;}
            UserManager & user(){return user_;}
            RequestManager & request(){return request_;}
        private:
            ConnectionManager con_;
            DataManager data_;
            UserManager user_;
            RequestManager request_;
    };
    La classe RequestManager sera (pour ce qui est du login) proche de
    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
    class RequestManager
    {
        public:
            bool login ( std::string const & login, std::string  const & password)
            {
                /* on vérifie si la connexion est active */
                if(!con_->active())
                    throw InactiveConnexion();
                /* on peut envisager de vérifier qu'il n'y a pas quelqu'un d'autre
                 * qui est connecté :D
                 */
                if(user_->user_)
                {
                    con_->send(DisconnectUser(user_->user_->name()));
                    user_->unlog();
                }
                respons rep=con_->send(ConnectUser(login, password);
                /* si c'est pas accepté, on renvoie directment false à l'IHM */
                if(!rep->accepted())
                    return false;
                /* envoi du résultat au gestionnaire d'utilisateur et 
                 * réponse à l'IHM
                 */
                return user_->create(login, rep->userType());
            }
            void unlog()
            {
                user_->destroy();
            }
        private:
            UserManager *user_;
            ConnectionManager *con_;
    }
    Et le gestionnaire d'utilisateurs sera fort proche de
    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
    class UserManager
    {
        friend class RequestManager;
        public:
            /* une foule de fonctions renvoyant un GO ou un STOP */
            bool canDoSomething() const{return user_->canDoSomething();}
            /* éventuellement le login de l'utilisateur connecté */
            std::string const& userLogin()const{return user_->login();}
        private:
            /* accessible uniquement depuis le gestionnaire de requêtes grace
             * à l'amitié
             */
            bool create(std::string const & name, eUserType type)
            {
                switch(type)
                {
                    case user:
                        user_=new User(/* paramètres éventuels */);
                        break;
                    case ope:
                        user_= new Operator(/* paramètres éventuels */);
                        break;
                     case admin:
                        user_= new Admin(/* paramètres éventuels */);
                }
     
            }
            void destroy()
            {
                delete user_;
                user_=NULL;
            }
            User * user_;
    }
    Et, fatalement, le formulaire de connexion agira sous une forme proche de (il accède à Metier par un pointeur dont il dispose )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void MyForm::onLoginButtonClick()
    {
        if(! Metier->request().login(champsLogin.text(), champPassword.text())
            MessageBox("impossible de se connecter");
        else
            MessageBox("vous êtes maintenant connecté");
    }
    (nota: Vu que tu travailles avec Qt, vérifie comment s'utilise QMessageBox )
    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 Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    10 246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 10 246
    Par défaut
    Merci beaucoup d'avoir passé autant de temps à répondre à ma question
    C'est vraiment impressionnant

    J'ai tout lu, mais je crois que je vais devoir le refaire plusieurs fois, parce qu'il y a vraiment beaucoup d'information à assimiler.

  5. #5
    Membre prolifique
    Avatar de Ryu2000
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2008
    Messages
    10 246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2008
    Messages : 10 246
    Par défaut
    En fait j'ai du mal m'éxpliquer, parce que ça je l'ai déjà fais, même si ça ne ressemble pas précisement à ça.

    L'utilisation rentre dans l'IHM, son login et son mot de passe.
    L'IHM le renvoit à la classe MySql qui renvoie 1 si l'utilisateur est présent dans la base ou 0 si ce n'est pas le cas.
    Après si MySql à renvoyé 1, l'IHM redemande à MySql le rang de l'utilisateur.
    Et l'IHM récupère le rang.

    Donc là j'arrive bien à 5.c.1 ?

    Bon par contre mon code ressemble à rien.
    Dans la classe MySql j'ai une méthode Operateur qui renvoie 1 si il est opérateur.
    Et une méthode Administrateur qui renvoie 1 si il est administrateur.

    Et du coup ça donne ça :
    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
     
            int Connexion=MySql2.ConnexionUtilisateur(Login, Password);
    	if(Connexion==1)
    	{
    		if((MySql2.Operateur(Login) == 1) || (MySql2.Administrateur(Login) == 1))
    		{
    			if(MySql2.Operateur(Login) == 1)
    			{
    				Operateur *Operateur3 = new Operateur;
    				Operateur3->SetLogin(Login);
    				Operateur3->SetPassword(Password);
    				Operateur3->SetRang();
    			}
    			if(MySql2.Administrateur(Login) == 1)
    			{
    				Administrateur *Administrateur3 =  new Administrateur;
    				Administrateur3->SetLogin(Login);
    				Administrateur3->SetPassword(Password);
    				Administrateur3->SetRang();				
    			}
    		}
    		else
    		{
    			Utilisateur *Utilisateur3 = new Utilisateur;
    			Utilisateur3->SetLogin(Login);
    			Utilisateur3->SetPassword(Password);
    			Utilisateur3->SetRang();			
    		}
    	}
    Le truc, c'est que j'ai deux fenetres là je suis dans la mainwindows, mais après l'IHM c'est dialogimpl.

    Donc faudrait que je fasse une méthode qui renvoit l'adresse du pointeur de l'utilisateur ?
    Je voudrais dans dialogimpl, récupérer le pointeur d'utilisateur, d'operateur ou d'administrateur selon ce qui à été créé dans mainwindows, mais je ne vois vraiment pas comment faire...

    =============================================================
    J'ai repris le code d'un professeur pour les fenêtres mais apparemment, c'est pas du tout ce qu'il y a de mieux.

    Dans le main on fait int pasla=1, après il est déclaré en extern dans mainwindows et dialogimpl.

    Dans mainwindows (pour passer de mainwindows à dialogimpl):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    if (pasla)
    {
    	pasla=0;
    	DialogImpl *winfille=new DialogImpl;
    	winfille->show();
    	hide();
    }
    Dans dialoimpl (pour repasser à mainwindows) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    pasla=1;
    hide();	
    MainWindowImpl *winmere=new MainWindowImpl;
    winmere->show();
    A chaque fois c'est des nouvelles instances...
    Je devrais peut être déclarer dialogimpl dans le main, comme mainwindows, ou dire dans le .h de mainwindows qu'elle a dialogimpl dans ses arguments...

    Edit :
    J'ai éssayé tout l'après midi, mais je trouve rien du tout...
    Il y a t'il moyen qu'une instance de classe renvoie sa propre adresse ?
    Parce que dans le main il y a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MainWindowImpl win;	
    win.show();
    Est-ce que je pourais faire un pointeur de type MainWindowImpl avec l'adresse de win ?
    Au lieu de faire new à chaque fois, j'aimerais garder win.
    Je pense qu'après j'arriverais à faire pareil avec dialogimpl et du coup peut être régler mon problème de partage d'une instance d'utilisateur entre mainwindow et dialog.

    Oh purée, je suis en train de me dire qu'au lieu de mettre this-> si j'avais mis this j'aurais réussi tout à l'heure...

    Au final je tourne en rond.
    Je pense que si j'arrivais à garder une seule instance de mainwindow et de dialog ce serait plus simple, mais ce n'est peut être même pas le cas.
    En tout cas je créer un utilisateur dans mainwindow et je dois le récupérer dans dialog et j'y arrive pas...

  6. #6
    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
    Citation Envoyé par thierrybenji Voir le message
    En fait j'ai du mal m'éxpliquer, parce que ça je l'ai déjà fais, même si ça ne ressemble pas précisement à ça.

    L'utilisation rentre dans l'IHM, son login et son mot de passe.
    L'IHM le renvoit à la classe MySql qui renvoie 1 si l'utilisateur est présent dans la base ou 0 si ce n'est pas le cas.
    Après si MySql à renvoyé 1, l'IHM redemande à MySql le rang de l'utilisateur.
    Et l'IHM récupère le rang.
    Pourquoi faire cela en deux temps

    Ton serveur devra sans doute gérer plusieurs connexions simultanées, et si tu dois faire deux reuqêtes ("le login existe-t-il " puis "quel est le rang de l'utilisateur qui existe "), tu augmente considérablement le risque de surcharger inutilement le serveur

    Pourquoi ne pas, simplement, renvoyer directement renvoyer:
    • 0 si l'utilisateur n'existe pas ousi le mot de passe ne correspond pas
    • 1 si c'est un utilisateur "simple"
    • 2 si c'est un "opérateur"
    • 3 si c'est un "administrateur"
    (ou toute autre possibilité de valeur cohérente)

    De toutes manières, les possibilités sont "mutuellement exclusives":
    • Si l'utilisateur n'existe pas, ou sil le mot de passe ne correspond pas, tu ne peux pas créer un utilisateur.
    • Un utilisateur simple ne peut être ni un opérateur ni un administrateur et il doit exister
    • Un opérateur est, effectivement un utilisateur, mais avec des droits particuliers, et ne peut pas être un administrateur
    • un administrateur, enfin, est effectivement un opérateur (et donc un utilisateur) mais avec encore plus de droits


    L'avantage, c'est que cela peut parfaitement être représenté sous la forme d'une énumération:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    enum eUserType
    {
        utInexistant = 0; //mauvais login ou password ne correspond pas
        utUser; // utilisateur "simple"
        utOperator; // utilisateur et opérateur
        utAdministrator; // opérateur et administrateur
    };
    Donc là j'arrive bien à 5.c.1 ?
    D'une certaine manière, oui, mais...
    Bon par contre mon code ressemble à rien.
    Dans la classe MySql j'ai une méthode Operateur qui renvoie 1 si il est opérateur.
    Et une méthode Administrateur qui renvoie 1 si il est administrateur.

    Et du coup ça donne ça :
    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
     
            int Connexion=MySql2.ConnexionUtilisateur(Login, Password);
    	if(Connexion==1)
    	{
    		if((MySql2.Operateur(Login) == 1) || (MySql2.Administrateur(Login) == 1))
    		{
    			if(MySql2.Operateur(Login) == 1)
    			{
    				Operateur *Operateur3 = new Operateur;
    				Operateur3->SetLogin(Login);
    				Operateur3->SetPassword(Password);
    				Operateur3->SetRang();
    			}
    			if(MySql2.Administrateur(Login) == 1)
    			{
    				Administrateur *Administrateur3 =  new Administrateur;
    				Administrateur3->SetLogin(Login);
    				Administrateur3->SetPassword(Password);
    				Administrateur3->SetRang();				
    			}
    		}
    		else
    		{
    			Utilisateur *Utilisateur3 = new Utilisateur;
    			Utilisateur3->SetLogin(Login);
    			Utilisateur3->SetPassword(Password);
    			Utilisateur3->SetRang();			
    		}
    	}
    Il y a beaucoup à dire sur ce code, je vais essayer d'être bref, mais je prévois déjà de faire un roman ... va donc te servir un café, t'allumer une cloppe, faire un petit pipi, bref, prépare toi à lire les choses avec attention

    D'abord, il existe un principe nommé "Open Closed principle" qui dit que ton code doit être "ouvert aux évolutions" mais "fermé aux modifications".

    Cela signifie que tu dois veiller à ce que, une fois que ton code fonctionne, tu puisse "relativement facilement" apporter des évolutions (par exemple: ajouter des types d'utilisateur ou permettre d'utiliser d'autres types de base de données), mais que ces évolutions ne doivent pas appliquer de modifications majeures dans le code qui fonctionne.

    Par exemple, que se passerait-il si, dans trois mois, tu décidais d'abandonner MySQL au profit de MsSQL, de Oracle ou de PostGreSQL

    Pire, que se passerait-il si, encore trois mois plus tard, tu décidais de permettre à l'utilisateur de choisir d'utiliser au choix un de ces différents SGBDR

    La réponse est simple: tu devrait aller modifier le code de ta fonction, qui fonctionne pourtant correctement au risque de... produire quelque chose qui ne fonctionne plus ou qui provoque des régressions

    Une des conséquences directes de cela est un autre principe souvent connu sous le nom de "principe de la responsabilité unique"

    Pour faire simple, ce principe te dit que, si une classe ou une fonction a plus d'une responsabilité (fait plus d'une chose à chaque fois), c'est qu'elle a sans doute... trop de responsabilités.

    On peut, bien sur, discuter sur la "granularité" de ce que l'on considère comme responsabilité, mais le fait de:
    • devoir s'occuper de la saisie (du mot de passe et du login)
    • devoir s'occuper de la requête au serveur
    • devoir s'occuper de la création de l'utilisateur
    représente clairement autant de responsabilités différentes qu'il est préférable de déléguer, pas seulement en faisant des fonctions différentes dans la classe de ton formulaire, mais, carrément dans autant de classes différente:
    • s'occuper de la saisie, c'est clairement la (seule) responsabilité de l'IHM, et plus particulièrement du formulaire ad-hoc
    • s'occuper de la requête au serveur, c'est clairement le rôle... d'une classe qui centralise l'ensemble des demandes de requêtes qu'il faut exécuter, qui les formate pour le serveur et qui les envoie.
    • s'occuper de la création (et de la destruction) de l'utilisateur, voire, de la gestion des utilisateurs connectés (si tu accepte l'idée que plusieurs utilisateurs puissent être connectés en même temps depuis le même ordinateur et la même application), c'est clairement le rôle d'une classe qui... ne fait que s'occuper des utilisateurs.

    De plus, il existe un principe nommé RAII (Ressource Acquisition Is Initialization) qui conseille de faire en sorte qu'un objet soit utilisable directement après sa création, sans devoir penser à le modifier.

    Il est à mettre en relation directe avec la "loi demeter" qui conseille de n'exposer que le stricte minimum indispensable d'une classe.

    Le login d'un utilisateur, par exemple, fait partie intégrante de l'utilisateur: si tu changes le login, tu change d'utilisateur, et donc, potentiellement l'ensemble des droits dont il dispose.

    Or, lorsque le serveur accepte la connexion d'un utilisateur, il décide d'accepter la connexion parce que le login et le mot de passe correspondent (autrement, il aurait refusé la connexion), et il accorde certains droits à l'utilisateur identifié par le login... pas à un autre...

    Si tu change de login, tu dois donc avoir... un autre utilisateur, qui ne peut être créé qu'après... accord de la part du serveur.

    De la même manière, le mot de passe d'un utilisateur ne devrait jamais franchir la "barrière" que représente le serveur lorsque celui-ci répond à une requête:

    Le mot de passe d'un utilisateur est déjà présent (d'une manière ou d'une autre) sur le serveur, et c'est déjà amplement suffisant!!! Chaque endroit où ce mot de passe sera "dupliqué" représente un risque supplémentaire de voir quelqu'un (de mal intentionné)... le récupérer de manière frauduleuse

    La partie "client" de ton application, et donc a fortiori ton IHM, n'a aucun besoin de maintenir ce mot de passe en mémoire.

    Par contre, il n'est pas impossible que le serveur renvoie une information permettant d'identifier de manière unique la connexion qui sert à l'utilisateur connecté (variable de session numéro de connexion )

    La classe "utilisateur" peut (doit) sans doute disposer de cette information , mais, l'utilisateur de l'application n'a a priori aucun droit de pouvoir récupérer cette information (au travers de l'IHM) et... encore moins le droit de la modifier.

    Les fonctions setLogin, setPassword et setRang n'ont donc strictement aucun sens pour un utilisateur: ce sont autant de choses qui doivent être réglées une bonne fois pour toutes lors... de la construction de l'objet (en passant les informations au constructeur )

    Parmi ces trois fonctions, la pire, c'est setRang...

    En effet, le rang est implicite par rapport au type d'utilisateur utilisé:

    Si tu crées un "opérateur", le rang est automatiquement... celui d'un opérateur et si tu crées un "administrateur", le rang sera automatiquement... celui d'un administrateur.

    C'est, typiquement, le genre de chose qui doit être fixée une bonne fois pour toutes... au moment de la création de ton objet

    Enfin, je ne suis pas particulièrement partisan de l'utilisation des if en pagaille si je peux faire autrement: je préfères utiliser un switch case chaque fois que possible, pour la simple et bonne raison que cela peut permettre certaines optimisations dans certains cas.

    Or, il serait tout à fait possible d'envisager un switch case dans le cas présent, surtout si tu passe par une énumération, et cela te permet en outre d'assurer une certaine vérification de la réponse:
    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
    UserManager::create user(Request const & r, std::string const & login)
    {
        eUserType rang=/* récupération du rang renvoyé */
        switch(rang)
        {
            case utInexistant:
                /* ca correspond pas, un petit message et on quitte la fonction
                 */
                messageBox("login inexistant ou mot de passe ne correspond pas");
                break;
            case utUser:
                /* c'est un utilisateur simple, on crée donc... un utilisateur 
                 * simple
                 */
                m_user=new Utilisateur(login/* , paramètres éventuels */);
                break;
            case utOperator:
                /* c'est un opérateur, on crée donc... un opérateur */
                m_user=new Operateur(login/* , paramètres éventuels */);
                break;
            case utAdministrator:
                /* c'est un opérateur, on crée donc... un administrateur*/
                m_user=new Administrateur(login/* , paramètres éventuels */);
                break;
            default :
                /* toutes les autres valeurs possibles: "YAUNOS", la communication
                 * a sans doute été corrompue.
                 * on envoie un petit message, et c'est tout
                 */
                messageBox("Résultat incohérent... communication corrompue");
                break;
        }
    }
    Le truc, c'est que j'ai deux fenetres là je suis dans la mainwindows, mais après l'IHM c'est dialogimpl.
    Et alors

    Les différents gestionnaires que je te propose ne doivent même pas faire partie d'une fenêtre particulière, ou, si c'est le cas, il "suffit" de les transmettre à celles qui en ont besoin
    Donc faudrait que je fasse une méthode qui renvoit l'adresse du pointeur de l'utilisateur ?
    Non... l'utilisateur, au niveau de l'IHM, ce n'est qu'une donnée, et donc un "détail d'implémentation" qui ne devrait pas sortir de ton "gestionnaire d'utilisateur".

    Et le gestionnaire d'utilisateur se contente de répondre (par "oui" ou par "non") à des questions simple "l'utilisateur peut il commander", "peut il émettre une note de crédit","peut il rajouter de nouveaux articles","peut il ...") voire, qui renvoie une valeur énumérée dans... eUserType (selon mon exemple)...
    Je voudrais dans dialogimpl, récupérer le pointeur d'utilisateur, d'operateur ou d'administrateur selon ce qui à été créé dans mainwindows, mais je ne vois vraiment pas comment faire...
    Tu n'as pas besoin de savoir quel type réel d'utilisateur a été créé...

    Tu dois "simplement" adapter les différents comportements de l'utilisateur en fonction du type réellement utilisé...

    C'est ce que l'on appelle le polymorphisme.

    Je te conseille de te reporter à la FAQ pour savoir comment le mettre en oeuvre et ce que cela implique


    J'ai repris le code d'un professeur pour les fenêtres mais apparemment, c'est pas du tout ce qu'il y a de mieux.

    Dans le main on fait int pasla=1, après il est déclaré en extern dans mainwindows et dialogimpl.

    Dans mainwindows (pour passer de mainwindows à dialogimpl):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    if (pasla)
    {
    	pasla=0;
    	DialogImpl *winfille=new DialogImpl;
    	winfille->show();
    	hide();
    }
    Dans dialoimpl (pour repasser à mainwindows) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    pasla=1;
    hide();	
    MainWindowImpl *winmere=new MainWindowImpl;
    winmere->show();
    A chaque fois c'est des nouvelles instances...
    Je devrais peut être déclarer dialogimpl dans le main, comme mainwindows, ou dire dans le .h de mainwindows qu'elle a dialogimpl dans ses arguments...
    Avec si peu de code, il est difficile de t'aider efficacement

    Pourrais tu nous en donner un peu plus (entre autres, les définitions de MainWindowImpl et de MainWindowImpl, et la fonction main() )

    Edit :
    J'ai éssayé tout l'après midi, mais je trouve rien du tout...
    Il y a t'il moyen qu'une instance de classe renvoie sa propre adresse ?
    Parce que dans le main il y a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    MainWindowImpl win;	
    win.show();
    oui, il y a moyen: toute fonction membre d'une classe dispose du pointeur this, qui est un pointeur sur l'objet courent
    Est-ce que je pourais faire un pointeur de type MainWindowImpl avec l'adresse de win ?
    Au lieu de faire new à chaque fois, j'aimerais garder win.
    Je pense qu'après j'arriverais à faire pareil avec dialogimpl et du coup peut être régler mon problème de partage d'une instance d'utilisateur entre mainwindow et dialog.

    Oh purée, je suis en train de me dire qu'au lieu de mettre this-> si j'avais mis this j'aurais réussi tout à l'heure...
    A l'intérieur d'une fonction membre, le recours à this est explicite (sauf quelques subtilités dont il vaut mieux ne pas parler ici ).

    Par contre, ici, tu es simplement pris d'une "diarhée mentale" occasionnée par la panique, dans laquelle il devient difficile de faire le tri (ne te méprend pas, mon but n'est pas de te vexer d'une quelconque manière... Ce n'est qu'une constatation que l'on fait souvent ) ...

    Respire un bon coup, calme toi, et essaye de nous expliquer clairement ton problème (si possible, en nous donnant un peut de code pour voir où tu te trompes )
    Au final je tourne en rond.
    Je pense que si j'arrivais à garder une seule instance de mainwindow et de dialog ce serait plus simple, mais ce n'est peut être même pas le cas.
    En tout cas je créer un utilisateur dans mainwindow et je dois le récupérer dans dialog et j'y arrive pas...
    Il est tout à fait possible de le faire...

    Mais, encore une fois, il est difficile de t'aider à y arriver sans un état des lieux complet de l'existant
    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

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

Discussions similaires

  1. Utilisation de classes utilisateur en IDL
    Par tdudouet dans le forum CORBA
    Réponses: 3
    Dernier message: 21/10/2008, 10h04
  2. pointeur sur une classe utilisateur
    Par maa dans le forum C#
    Réponses: 15
    Dernier message: 06/07/2007, 16h25
  3. Classe Utilisateur et UML
    Par SOXI dans le forum UML
    Réponses: 1
    Dernier message: 31/12/2006, 18h18
  4. Réponses: 2
    Dernier message: 14/11/2006, 15h59
  5. Class template hérité
    Par Azharis dans le forum Langage
    Réponses: 4
    Dernier message: 24/06/2005, 22h03

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