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 :

Encapsulation pour donner des accès minimum à chaque classe


Sujet :

C++

  1. #1
    Membre averti Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Points : 358
    Points
    358
    Par défaut Encapsulation pour donner des accès minimum à chaque classe
    Salut !

    J'ai un problème d'encapsulation, je n'arrive pas à trouver de moyen pour que chaque classe n'est accès qu'as ce qu'il a besoin.

    Voici le code :
    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
     
    // Charger de s'occuper de "type value_" (création / suppression / stockage)
    class Value
    {
    	public :
    		Value(type value) : value_(value) {}
     
    	private :
    		type value_;
    };
     
    // classe mere qui sera hérité par beaucoup de classe fils
    class Mere
    {
    	public :
    		Mere(type t) : v_(t)
    		{
    		}
    		//...
     
    	private :
    		Value v_;
    		//...
    };
     
    // Cette classe et ses descendants doivent être les seul qui on le droit d'accéder à "Value::value_"
    class SeulClassQuiUtiliseValue
    {
    	public :
    		void getValue(A &a)
    		{
    			// besoin d'utiliser A.v.value_
    		}
    };
    Quelque solution :
    1) Créer des accesseurs de "v_" dans "Mere" et de "value_" dans "Value".
    Problème : Toute les classe qui hérite de la classe "Mere" auront un moyen d'accéder également a "value_" en utilisant les accésseurs.

    2) Mettre SeulClassQuiUtiliseValue::getValue(A &a) comme amie de "Mere" et de "Value"
    Problème : Cette méthode "getValue" auras un accès complet au membre privé de "Mere" alors que seulement "v_" nous intéresse.

    3) Créer un accésseur de "V_" dans classe "Mere" et mettre SeulClassQuiUtiliseValue::getValue(A &a) comme amie de "Value".
    Problème : Toute les classe qui hérite de la classe "Mere" auront accès a "v_".
    Bien qu'ils ne puissent rien en faire directement car pas accès a "value_",
    on pourrais être tenté d'utiliser ce "v_" comme argument pour une méthode de "SeulClassQuiUtiliseValue" (ou une classe dérivé).
    Ainsi, la classe Value jouerait un rôle dans les classe dérivé de la classe "Mere" alors quel ne devrais pas.


    Les trois solutions au quel j'ai pensé présente toutes des inconvenants...
    Existerait il d'autre solutions pour résoudre ce problème ?
    Et si aucune autre solution existe, quel serais la meilleur ?

    Merci pour votre aide.

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    En fait, il y a un mix de tout à faire:
    1. Rendre la classe Value non copiable et non assignable ( == déclarer sans les définir le constructeur par copie et l'opérateur d'affectation dans l'accessibilité private)
    2. Déclarer le membre de type value uniquement dans la classe qui en a besoin (dans l'accessibilité priavate)
    3. Déclarer la classe qui a besoin de Value::value_ (ou une de ses méthodes private) amie de la classe Value

    Cela prendrait la forme 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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    class Value; // déclaration anticipée de la classe, pour qu'elle soit connue
                 // lorsque le compilateur la rencontre dans ClassQuiUitiliseValue
    class Mere
    {
        /*aucune référence à la classe Value ici */
        public:
            Mere(){}
            virtual ~Mere(){}
            /* méthodes publiques  */
        private:
            /* membres et méthodes privées */
    };
    class ClassQuiUitiliseValue : public Mere
    {
        public:
            ClasseQuiUtiliseValue( type v): val_(v){}
            virtual ~ClasseQuiUtiliseValue(){}
            /* les méthodes qui vont bien et qui peuvent être publiques */
        private:
            /* Seule ClasseQuiUtiliseValue pourra accéder à cette méthode
             * mais son implémentation doit être déportée après la définition
             * réelle de la classe Value 
             */
            /* inline */ le_type_qui_va_bien function();
            Value val_;
    };
    class Value
    {
        public : 
            /* il pourrait être private... mais donnons nous l'occasion de
             * quand meme la construire
             */
            Value(type value_):value_(value_){}
            /* d'office public, sinon nous aurons un problème */
            ~Value(){}
        private:
            type value_;
            /*La copie est interdite  */
            Value(const Value&);
            /* l'affectation itou */
            Value operaor= (const Value&);
            friend le_type_qui_va_bien ClasseQuiUtiliseValue::function();
    };
    /*inline */ le_type_qui_va_bien ClasseQuiUtiliseValue::function()
    {
        /* ce qu'il faut faire, en pouvant très bien envisager un code proche de*/
        type temp = val_::value_;
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Pour te permettre d'en comprendre un peu plus sur l'amitié...

    Si tu déclare une classe amie, il faut savoir que l'amitié n'est ni transitive, ni réciproque, ni héritée (et c'est ce dernier terme qui nous intéresse).

    Cela signifie que seules les méthodes qui peuvent être utilisées en faisant passer une instance d'une autre classe pour une instance de la classe déclarée amie pourront envisager d'accéder aux membres et méthodes non publiques.

    En outre, une classe dérivée n'a acces qu'aux membres et méthodes publiques ou protégés (mais en aucun privés) de la classe dont elle dérive.

    Ainsi, si tu as un code 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
     
    class Mere
    {
        public:
            /*...*/
            void foo();
            type1 publicMember;
        protected:
            void bar();
            type2 protectedMember;
        private:
            void privateFoo();
            type3 privateMember;
    };
    class Derivee : public Mere
    {
        /*...*/
    };
    la classe Derivee ne pourra accéder qu'à foo, bar, publicMember et protectedMember...

    Si même, tu en venais à rendre (par exemple) foo virtuelle et à la spécialiser (dans une optique de polymorphisme) dans dérivée, tu devrais passer par Mere::foo pour avoir réellement acces aux membres non public de la classe qui déclare mère amie.

    Ainsi, un code 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
    35
    36
    37
    38
    39
    40
    41
    class Value
    {
        public:
            friend class Mere;
            Value(int i):i(i){}
            ~Value(){}
        protected:
        private:
            int i;
    };
     
    class Mere
    {
        public:
    	Mere(const Value& v):v(v){}
            virtual ~Mere(){}
            virtual void foo(){cout<<"dans mere "<< v.i<<endl;}
        protected:
            virtual void bar(){}
            Value v;
        private:
     
    };
    class Fille : public Mere
    {
        public:
            Fille(const Value& v):Mere(v){}
            virtual ~Fille(){}
            virtual void foo()
            {
                cout<<"dans fille "<< v.i<<endl; //refusé
                /* par contre ce qui suit serait accepté
                 * cout<<"Fille appelle Mere::foo ";
                 * Mere::foo();
                 */                          
            }
        protected:
            virtual void bar(){}
        private:
     
    };
    refusera de compiler au prétexte que v::i est privé dans le contexte de Fille::foo (bien quelle puisse accéder à v, étant donné que le membre est protégé)...

    Si, en plus, tu venais à placer v (ou la fonction qui doit y accéder) dans l'accessibilité privée de Mere, Fille ne pourrait même pas accéder ni à v ni à la fonction qui tente d'y accéder (et cette fonction ne pourrait d'ailleurs pas être spécialisée dans la classe dérivée)

  4. #4
    Membre averti Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Points : 358
    Points
    358
    Par défaut
    Un grand merci koala pour ta réponse =)

    Citation Envoyé par koala01 Voir le message
    1. Rendre la classe Value non copiable et non assignable ( == déclarer sans les définir le constructeur par copie et l'opérateur d'affectation dans l'accessibilité private)
    2. Déclarer le membre de type value uniquement dans la classe qui en a besoin (dans l'accessibilité priavate)
    3. Déclarer la classe qui a besoin de Value::value_ (ou une de ses méthodes private) amie de la classe Value
    2> Ha oui, effectivement, en modifiant un peu ma conception pour faire en sorte que ce soit "ClassQuiUitiliseValue" qui stock également "Value" ça arrange pas mal de chose ! =)
    3 > Mais, du coup, si on arrive à mettre en place le second point, ça ne sert plus à rien de protéger d'avantage la classe "Value" non ? vue que SEUL "ClassQuiUitiliseValue", stock et utilise "Value".
    1> Pareil que pour le points 3, si on arrive à mettre en place le point 2, je n'arrive pas à voir à quoi sert cette sécurité supplémentaire ?

    Une dernière question, si jamais, la conception fait qu'on ne puisse pas utiliser le point (2), comment faire ? (si il existe des cas ou on ne puisse pas appliquer le point (2) ?)


    Merci pour votre aide.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Il est vrai que l'utilité du point 1 est discutable, mais c'est surtout pour éviter la tentation d'écrire une méthode dans une autre classe qui tenterait de créer une instance de Value (c'est d'ailleurs la raison pour laquelle je verrais bien placer le constructeur en accessibilité privée, ainsi, seule les classes déclarées amies pourront créer une instance de Value )

    Le point 3 est obligatoire, si tu veux que la classe qui utilise la classe Value puisse... accéder à la valeur du membre (car, si tu crées un accesseur pour ce membre, tu permet à n'importe quelle classe d'y accéder )

  6. #6
    Membre averti Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Points : 358
    Points
    358
    Par défaut
    Ha ok, ca y est je crois que j'ai compris, en faite tu te place au niveau de la classe "Value" et pas au niveau du projet dans sa globalité.

    Au lieu de dire : "C'est à la classes qui contient 'Value' que revient la responsabilité de bien utiliser m'utiliser".
    Tu dit : "C'est à la classes 'Value' de dire qui à le droit de m'utiliser (qui a la responsabilité de bien m'utiliser)".


    C'est bien ça ?

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    C'est effectivement quelque chose dans le genre...

    Mais, à vrai dire, je n'ai pas énormément le choix, étant donné que tu souhaite ne pas placer d'accesseur sur les membre de Value... il faut donc bien décider à un moment de qui aura la responsabilité d'autoriser (ou non) l'accès aux données

  8. #8
    Membre averti Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Points : 358
    Points
    358
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Mais, à vrai dire, je n'ai pas énormément le choix, étant donné que tu souhaite ne pas placer d'accesseur sur les membre de Value... il faut donc bien décider à un moment de qui aura la responsabilité d'autoriser (ou non) l'accès aux données
    Ha, non non, c'est pas que je ne souhaite pas avoir d'accesseur, c'est que dans mon message initiale, j'avais une conception ou, la classe qui stockait "Value" n'était pas celle qui l'utilisai.
    Du coup, si j'utilisais un accesseur, tout les classe dérivais aurais pu avoir accées a "Value".

    Mais après modification de la conception, "Value" se trouve dans la classe qui va l'utiliser, et donc déclaré private. (plus de problème d'accès indésirable à partir des classe dérivée)


    J'aurais donc une dernier question, dans le cas de la seconde conception, ou "Value" seras stocké en private dans des classes qui l'utiliserons directement.
    Vaut il mieux utiliser des accesseurs ? ou utiliser des déclarations d'amitiés ?

    Encore Merci.

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut la réponse de normand
    Cela dépend énormément du cas réel...

    Je vais prendre deux cas "exagérés", mais qui te permettront, je l'espère, de comprendre l'idée générale.

    D'un coté, tu sais que tu n'aura jamais qu'une et une seule classe qui devra être en mesure de
    déclarer une variable du type Value et de créer l'instance qui va avec
    d'accéder aux membres de la classe Value.
    (le cas peut être étendu à "un nombre très restreint (mettons jusqu'à deux ou trois) et connu à l'avance" de classe disposant de ces capacités)

    Et d'un autre coté, tu as le cas où tu te rend compte que le nombre de classe devant "utiliser" (dans le sens le plus large du terme) la classe est "important" (disons, pour fixer une limite, supérieur à deux ou trois) ou susceptible d'évoluer (le fameux "tiens, il me manque une classe... qui a besoin de Value ")

    Dans le premier cas, donc, tu peux envisager l'amitié, et, pourquoi pas, en profiter pour placer le constructeur dans l'accessibilité privée, voire, pour faire de la classe Value une classe imbriquée dans la (l'une des) classe(s) déclarée(s) amie(s)

    De cette manière, tu verrouille totalement les possibilités d'utilisation de la classe Value dans un ensemble de circonstances très strictes.

    Dans le second cas, il deviendra rapidement lourd et fastidieux de gérer les possibilités à coup d'amitié.

    Le mieux que tu auras à faire est donc de laisser la classe Value en "libre circulation" (permettre, finalement, à n'importe quelle classe d'en créer une instance) mais à n'utiliser cette classe que comme membre privé pour les classes... qui en ont vraiment besoin (et de restreindre éventuellement l'accessibilité des méthodes qui utilisent ce membre).

    Si le premier cas aura pour conséquence de "figer" la situation, il est cependant clair que le fait d'envisager le second cas permet bien plus d'évolutions ultérieures (pe: l'ajout de classes qui n'avaient pas été envisagées lors des analyses précédentes, soit par oubli, soit par modifications des besoins)

    Je m'attend déjà à ce que certains s'écrient "au scandale" en lisant ces lignes.

    C'est la raison pour laquelle je veux insister sur un dernier point: je présente deux solutions contradictoires mais qui peuvent s'avérer "cohérentes" selon la situation que tu rencontre, mais je ne me risquerai surement pas à te conseiller la première solution qui est celle qui fera se lever les cheveux sur la tête de tous.

    Si tu décide de te tourner vers cette solution, c'est parce que tu estimes que c'est vraiment celle qui apporte la meilleure réponse à ton problème

  10. #10
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Salut,

    Citation Envoyé par Ekinoks Voir le message
    j'avais une conception ou, la classe qui stockait "Value" n'était pas celle qui l'utilisai.
    C'est peut-être ça un peu le problème à la base non ?
    Pour moi ce qui est important ce n'est pas de savoir qui stocke mais de savoir qui utilise. Quand un composant a besoin d'un autre composant pour travailler, je lui fournis lors de sa construction.
    Si il y a un seul 'client' alors c'est éventuellement possible d'encapsuler complètement, s'il y en a plusieurs on peut soit partager soit remanier le découpage pour faire en sorte qu'il n'y en ait qu'un.

    La meilleure manière de contrôler quels composants accèdent à un autre est de ne fournir ce dernier qu'à ces composants précis lors de leurs construction.
    Ça déplace la responsabilité au niveau de la construction et du laçage des composants entre eux, ce qui je trouve a l'énorme avantage de bien marquer la séparation entre code de construction et code d'exécution.

    MAT.

  11. #11
    Membre averti Avatar de Ekinoks
    Profil pro
    Étudiant
    Inscrit en
    Novembre 2003
    Messages
    687
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2003
    Messages : 687
    Points : 358
    Points
    358
    Par défaut
    Merci beaucoup à vous deux pour votre aide.

    koala> Ok, ca y est j'ai compris,
    Je vais pouvoir réfléchir a mon cas pour savoir quel solution utilisé ^^


    Citation Envoyé par Mat007 Voir le message
    C'est peut-être ça un peu le problème à la base non ?
    Oui, c'était effectivement ça le problème principale.

    Citation Envoyé par Mat007 Voir le message
    Quand un composant a besoin d'un autre composant pour travailler, je lui fournis lors de sa construction.
    C'est ce que je fais aussi habituellement, mais dans ce cas ci, la classe qui avais besoin d'utiliser le composant était une instance statique, qui représentais une interface avec une bibliothèque.
    mais j'ai finalement réussi a restructurer le projet pour que "la classe qui stockait 'Value' soit celle qui l'utilise".

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

Discussions similaires

  1. htaccess et php ( pour donner l'accès a un dossier)
    Par paterson dans le forum Langage
    Réponses: 1
    Dernier message: 07/03/2012, 14h07
  2. Des bénévoles pour donner des cours dans les langues : Français, Anglais et Arabe
    Par witch dans le forum La taverne du Club : Humour et divers
    Réponses: 15
    Dernier message: 24/02/2012, 23h47
  3. [FPDF] Quelle classe pour produire des PDF simples ?
    Par boteha dans le forum Bibliothèques et frameworks
    Réponses: 6
    Dernier message: 03/11/2005, 22h55
  4. Classe pour generer des logs
    Par freddyboy dans le forum MFC
    Réponses: 9
    Dernier message: 13/07/2005, 12h54
  5. Réponses: 2
    Dernier message: 19/01/2004, 12h19

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