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 avec constructeurs multiples


Sujet :

C++

  1. #21
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par lg_53 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    //Mask(const std::vector<int>& A,const std::vector<int>& B){
    //	Box<int> box(A,B);
    //	Mask(box);
    //	};
    Tout simplement en C++11 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Mask(std::vector<int> const & a, std::vector<int> const & b) : Mask(Box(a, b)) { }

  2. #22
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    à condition toute fois d'avoir un compilateur assez récent.

    Par contre, tu pourrais avoir une méthode static qui fais la même chose:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Mask Mask::create(std::vector<int> const & a, const std::vector<int>& b) {return Mask(Box(a, b));}
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #23
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 627
    Points : 10 551
    Points
    10 551
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    std::vector<int> n'a pas de fonction membre swap(). Voir ma proposition au dessus.
    Perdu

    std::vector::swap

  4. #24
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Autant pour moi, je corrige ça

  5. #25
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    La classe Box étant templatisée, il faut préciser le type associer lorsqu'on déclare un Box.
    Ceci dit donc, voici ma classe réécrite:

    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
    class Mask {
    public:
        //Mask() {};
        Mask(int sizex, int sizey, int originX=0, int originY=0):_sx(sizex),_sy(sizey),_ox(originX),_oy(originY){};
      	Mask(const Box<int>& box):_sx(box.sizeX()),_sy(box.sizeY()),_ox(box.GetA()[0]),_oy(box.GetA()[1]){};
      	//Mask(const std::vector<int>& A,const std::vector<int>& B):Mask(Box<int>(A,B)){}; // A partir de C++11
      	Mask(const std::vector<int>& A,const std::vector<int>& B){*this=createMask(A,B);};
     
      	Mask createMask(std::vector<int> const & a, const std::vector<int>& b) {return Mask(Box<int>(a, b));};
     
        ~Mask(){};
     
    protected:
    		int _sx,_sy,_ox,_oy;
    };
    En C++11 donc, on peut faire un appel à un autre constructeur déjà défini.
    Sinon, en réutilisant ta méthode static leternel, on pourrait imaginer écrire le constructeur comme ci-dessus.
    L'écriture est correcte? Car j'ai jamais utilisé un "*this=qqch"... Je suppose que ça fait appel au constructeur de copie de la classe, qui ici n'étant pas spécifié est donc celui par défaut qui se contente de recopier les attributs. Corriger moi si je me trompe.

    Mes premiers tests ont l'air de fonctionner avec cette façon de faire.

  6. #26
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    *this = va modifier l'objet précédemment construit, en l'écrabouillant.

    Ca n'est pas ça que tu souhaites.

    En fait, tu peux être plus extrême.
    Pour l'exercice, mets _sx et _sy en constantes. Et nomme les _width et _height, c'est quand même plus clair.
    (de même, ox et oy serait left et top).

    Dans ces conditions, ton constructeur n'a pas d'autre choix que d'être explicite sur _width et _height.
    Du coup, tu n'a pas d'autre choix que de ne pas proposer le constructeur à deux vecteur, mais de contraindre à l'usage de createMask.

    Au passage, comme tu es dans Mask, la fonction peut se limiter à create().
    Je pensais à une fonction libre.

    Les alternatives sont nombreuses:
    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
    //idiome des créateurs:
    namespace geom {
    class Mask {
    protected:
    	Mask(int width, int height, int originX=0, int originY=0):
    		width(width),height(height),left(originX),top(originY){}
    public:
    	~Mask(){};
     
    	static Mask create(int width, int height, int originX=0, int originY=0) {return Mask(width, height, originX, originY);}
    	static Mask create(Box<int> const& box) {return Mask(box.sizeX(), box.sizeY(), box.GetA()[0], box.GetA()[1]);}
    	static Mask create(std::vector<int> const & a, std::vector<int> const& b) {return create(Box<int>(a, b));}
    protected:
    	int width,height;
    	int left,top;
    };
    }//geom::
     
    using geom::Mask;
     
    int main() {
    	Mask a = Mask::create(...);
    	return 0;
    }
    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
    //idiome des constructeurs nommés:
    namespace geom {
    class Mask {
    protected:
    	Mask(int width, int height, int originX=0, int originY=0):
    		width(width),height(height),left(originX),top(originY){}
    public:
    	~Mask(){};
     
    	static Mask precise(int width, int height, int originX=0, int originY=0) {return Mask(width, height, originX, originY);}
    	static Mask like(Box<int> const& box) {return Mask(box.sizeX(), box.sizeY(), box.GetA()[0], box.GetA()[1]);}
    	static Mask around(std::vector<int> const & a, std::vector<int> const& b) {return create(Box<int>(a, b));}
    protected:
    	int width,height;
    	int left,top;
    };
    }//geom::
     
    using geom::Mask;
     
    int main() {
    	Mask a = Mask::precise(12,10,5,5);
    	return 0;
    }
    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
    //variante fonction libre des créateurs:
    namespace geom {
    class Mask {
    protected:
    	Mask(int width, int height, int originX=0, int originY=0):
    		width(width),height(height),left(originX),top(originY){}
    public:
    	~Mask(){};
     
    protected:
    	int width,height;
    	int left,top;
     
    	friend Mask create(int width, int height, int originX, int originY);
    	friend Mask create(Box<int> const& box);
    	friend Mask create(std::vector<int> const & a, std::vector<int> const& b;
    };
     
    inline Mask createMask(int width, int height, int originX=0, int originY=0) {
    	return Mask(width, height, originX, originY);
    }
    inline Mask createMask(Box<int> const& box) {
    	return Mask(box.sizeX(), box.sizeY(), box.GetA()[0], box.GetA()[1]);
    }
    inline Mask createMask(std::vector<int> const & a, std::vector<int> const& b) {
    	return create(Box<int>(a, b));
    }
     
    }//geom::
     
    int main() {
    	geom::Mask a = geom::createMask(...);
    	return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    //les factories, qui prolonge le précédent.
    namespace geom {
    class Mask {
    protected:
    	Mask(int width, int height, int originX=0, int originY=0):
    		width(width),height(height),left(originX),top(originY){}
    public:
    	~Mask(){};
     
    protected:
    	int width,height;
    	int left,top;
     
    	friend class MaskFactory;
    	friend class RepetitiveMaskFactory;
    };
     
    class MaskFactory {
    public:
    	Mask create(int width, int height, int originX=0, int originY=0) {
    		return Mask(width, height, originX, originY);
    	}
    	Mask create(Box<int> const& box) {
    		return Mask(box.sizeX(), box.sizeY(), box.GetA()[0], box.GetA()[1]);
    	}
    	Mask create(std::vector<int> const & a, std::vector<int> const& b) {
    		return create(Box<int>(a, b));
    	}
    };
     
     
    class RepetitiveMaskFactory {
    public:
    	const int width, height;
    public:
    	RepetitiveMaskFactory(int width, int height) :  width(width), (height) {}
     
    	Mask createAt(int x=0, int y=0) {
    		return Mask(width, height, x, y);
    	}
    	Mask createAt(Mask const& other) {
    		return Mask(width, height, other.left, other.top);
    	}
    };
    }//geom::
     
    using namespace geom;
    int main() {
    	MaskFactory factory;
    	Mask a = factory.create(50, 50, 8, 12);
     
    	RepetitiveMaskFactory creator(12, 18);
    	Mask b = creator.createAt(a);
    	Mask c = creator.createAt(1, 2);
     
    	return 0;
    }
    Dans les quatre cas, j'ai supprimé le problème de la construction, spécifique, vu que tu as déjà un constructeur direct.

    PS aux candidats rouspéteurs: c'est à dessein que j'utilise le même nom pour l'argument du constructeur direct est pour le champ.
    Le C++ le permet, et pour constructeur sans corps, c'est simple que jouer avec les préfixes/suffixes.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #27
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    Merci bien pour se tour d'horizon des possibilités.

    Si j'analyse, dans tous les cas, le constructeur de base de la classe est mit en protected.
    On contraint alors l'utilisateur à déclarer un objet Mask non pas via une déclaration du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Mask mon_mask(paramètre...)
    mais grâce à des méthodes de création d'objet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Mask mon_mask=Mask::create(paramètre...) // ou
    Mask mon_mask2=CreateMask(paramètre...)
    Pour ce qui est de la méthode des factories (que je ne connaissais pas et qui portent bien leur nom)
    on créer donc une classe dont le but est d'instancier un objet Mask. Une usine à Mask littéralement.
    Une certaine lourdeur dont je peux me passer ici je pense (autant faire le plus simple possible), mais je garde en tête cet exemple qui m'auras initier aux factories et qui me resserviras un jour j'en suis sûr.

    Pour les besoins que j'en ai je vais prendre ta solution proposée en premier (idiome des créateurs).

    Merci à vous tous pour vos réactions.

  8. #28
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    Les factories sont un des design pattern les plus classiques, parce qu'ils permettent de combiner deux niveaux d'informations: un paramétrage général et les cas précis.
    Il suffit que le paramétrage général soit conservé dans des champs internes.
    C'est ce que j'ai représenté avec RepetitiveMaskFactory.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Salut,
    Citation Envoyé par lg_53 Voir le message
    4) Pourquoi mettre en protected plutôt qu'en private? C'est un choix personnel. Tout simplement parce que si qqn n'a pas les droits pour modifier la classe mais qu'il souhaite l'améliorer,l'étoffer pour ses propres besoins, et bien il peut faire une classe fllle et a ainsi accès aux attributs librement en lecture et écriture. Là non plus, ce n'est pas ce qui me pose problème.
    Voilà une très mauvaise idée...

    De manière générale, tu ne devrais avoir que des comportements (AKA fonctions), éventuellement susceptibles d'être redéfinis dans les classes dérivées (AKA fonctions virtuelles) si cela fait sens dans l'accessibilité publique et dans l'accessibilité protégée, et si tu veux vraiment faire les choses de manière, tu essayeras de limiter les accesseurs (AKA "getter") au maximum et tu refuseras avec obstination toute solution à base de mutateurs (AKA setters).

    En effet, si tu décides de placer une variable membre dans une accessibilité autre que l'accessibilité privée, tu donnes implicitement le droit à "tout le monde" (même si tu mets une légère restriction au fait que tout le monde se réduit aux classes dérivées en ce qui concerne l'accessibilité protégée) d'aller modifier à sa guise la donnée en question. Cela conduit immanquablement à une perte totale du contrôle que tu peux avoir sur le nombre de fonctions qui iront la modifier, et rend toute tentative d'encapsulation et de programmation par contrat bancale.

    Si tu as du mal à comprendre ce que j'explique ou que tu n'arrives pas à comprendre les raisons qui m'incitent à te donner ce conseil, n'hésite pas à me le faire savoir, je développerai volontiers chaque point plus ou moins obscure
    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

  10. #30
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 186
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 186
    Points : 17 126
    Points
    17 126
    Par défaut
    Je suis tout à fait d'accord avec toi sur ce point.
    Pour moi, une variable est constante ou privée.
    Et à priori, même constante, jamais publique.

    Certain parleront des static const, mais je n'en vois jamais de vraie utilité.
    En général, ce sont des globales déguisées, et je préfère proposer des fonctions statiques qui s'en servent.

    Et, lg_53, quand koala01 dit développera, crois-le, car il s'en fera un plaisir... et toi un long moment de lecture
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  11. #31
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    Le dévoleppement d'un code bien encapsulé étant une très longue histoire (je vous crois) et n'étant pas le sujet de départ, je vais passé le topic en résolu pour ne pas perdre un lecteur qui passerait par là.

    Mon habitude de mettre protected au lieu de private provient certainement d'une expérience que j'ai eu: une fois, j'ai eu besoin d'améliorer une classe développée par quelqu'un d'autre, sans pouvoir la modifier. Sauf que dans cette classe tous les attributs était privés et la classe ne comportait aucun Setter.
    Résultat: j'ai dû dupliquer un bout de code beaucoup trop gros pour être normalement dupliqué, jusqu'à ce que l'auteur de cette classe ait changé le private en protected ce qui m'a permis de pouvoir écrire une classe fille. Voici donc pour la petite histoire.

    Merci encore à tous.

    lg_53

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par lg_53 Voir le message
    Le dévoleppement d'un code bien encapsulé étant une très longue histoire (je vous crois) et n'étant pas le sujet de départ, je vais passé le topic en résolu pour ne pas perdre un lecteur qui passerait par là.

    Mon habitude de mettre protected au lieu de private provient certainement d'une expérience que j'ai eu: une fois, j'ai eu besoin d'améliorer une classe développée par quelqu'un d'autre, sans pouvoir la modifier. Sauf que dans cette classe tous les attributs était privés et la classe ne comportait aucun Setter.
    Résultat: j'ai dû dupliquer un bout de code beaucoup trop gros pour être normalement dupliqué, jusqu'à ce que l'auteur de cette classe ait changé le private en protected ce qui m'a permis de pouvoir écrire une classe fille. Voici donc pour la petite histoire.
    Je comprend parfaitement que tu aies pu avoir une (voir plusieurs) expérience(s) malheureuse(s), mais elle(s) ne devrai(en)t pas te servir de prétexte à la prise de (très) mauvaises habitudes!!!

    Il faut savoir que la notion d'encapsulation est régie par une loi appelée loi de Déméter. Cette loi nous dit que, si une class A manipule en interne un objet de type B, l'utilisateur de la classe A ne devrait pas avoir à connaitre le type B pour pouvoir utiliser la classe A.

    A partir de là, il faut comprendre que ce ne sont pas les données qui ont de l'importance lorsque l'on décide de créer un agrégat de données (car on peut dire exactement la même chose pour les classes que pour les structures) : ce sont les services qu'il peut nous rendre et les ordres auxquelles il est susceptible de réagir.

    On peut à la limite classer les différentes fonctions qui nous permettent de manipuler une classe en deux grandes catégories : les fonctions qui nous permettent d'interroger l'objet afin d'en connaitre l'état à un point de vue donné et celles qui nous permettent d'agir afin de modifier un état particulier.

    Les accesseurs entrent bien évidemment dans la première, les mutateurs (setters) entrent quant à eux dans la seconde. Mais, pour ce qui concerne les accesseurs, il n'y a du sens à en proposer un que si l'on peut considérer que le fait d'être en mesure d'accéder à la donnée en question fait partie des services que l'on est en droit d'attendre de la part de la classe.

    Par exemple, si tu as une classe Coordonnée qui représente une coordonnée sur un référentiel à deux dimensions, nous sommes en effet en droit de nous attendre à être en mesure de récupérer l'abscisse et l'ordonnée correspondant à une coordonnée particulière.

    Mais si tu as une classe Voiture qui dispose (forcément) d'un membre de type ReservoirACarburant, il n'y a absolument aucun sens à fournir un accesseur sur le réservoir en question : on s'attend uniquement à ce que la voiture nous propose un certains nombre de services qui seront sans doute en relation avec la présence du réservoir, mais on ne s'attend absolument pas à être en mesure d'accéder directement au réservoir.

    Nous aurons une jauge à carburant nous indiquant la proportion existante entre la quantité actuelle de carburant et la contenance maximale du carburant, nous disposerons peut être de l'affichage d'une évaluation du ombre de kilomètres que l'on peut encore parcourir et nous aurons bien sur une trappe par laquelle nous pourrons rajouter du carburant qui finira forcément dans le réservoir, mais, à moins d'être mécanicien -- et encore -- on n'a absolument aucun besoin de pouvoir accéder directement au réservoir... si tant est qu'il y en a réellement un (comprend: qu'il existe bel et bien une classe Reservoir et que les données relatives au carburant soient bel et bien maintenues par un membre de ce type)

    A partir de là, on peut déjà se dire que, si l'on ne juge pas utile de fournir un accesseur sur un membre donné, il n'y a absolument aucune raison à fournir un mutateur sur cette donnée. Quant à celle pour lesquelles on a décidé de fournir un accesseur, le mutateur est sans doute la plus mauvaise des solutions que nous pourrions envisager.

    En effet, l'utilisation d'un mutateur implique que l'on crée un nouvel objet du type (correspondant au type du membre à modifier) dont on a besoin avant de le mettre dans un état "que l'on croit pouvoir être correct" et de fournir cet objet à l'objet que l'on veut modifier.

    Le gros problème de cette logique, c'est que tout le contrôle échoit à l'utilisateur : c'est à celui qui va utiliser le setter de s'assurer que l'élément transmis sera dans un état cohérent, alors qu'il n'a peut être qu'un idée trop vague de toutes les conditions qui permettront de dire sans l'ombre d'un doute que "cet état est compatible avec celui auquel on est en droit de s'attendre de la part de notre objet".

    Prenons l'exemple d'une d'une classe qui peut être positionnée dans un référentiel à deux dimensions pour te permettre de comprendre. Cette caractéristique est sans doute représentée au niveau des membres de la classe par un membre de type Coordonnée dont je parlais là tantôt. Si cette classe propose une fonction setPosition, c'est à l'utilisateur de s'assurer que la position est correcte, que l'instance de la classe peut effectivement se retrouver à la position indiquée.

    Mettons que la position en question corresponde au milieu d'un océan. Les seuls objets réellement susceptibles d'accéder à cette position sont les objets de type "navires" (ou sous marins) et de type "avion" (ou similaires), mais une voiture ne pourra en aucun cas accéder à cette position. Si l'utilisateur doit prendre en compte tous les types d'éléments positionnables et tous les caractéristiques des différents endroits afin de déterminer si un objet bien particulier peut effectivement accéder à une position donnée, je peux te garantir qu'il oubliera systématiquement une ou l'autre combinaison.

    Plutôt que de fournir un mutateur sur la position, nous avons donc intérêt à fournir un service "move(diffX, diffY)" ou "moveTo(posX, posY)" qui permettra à l'objet positionnable de s'assurer qu'il peut effectivement atteindre la position à laquelle on lui demande de se rendre. C'est notre objet qui devient responsable de la manière dont son état est modifié.

    La différence ne tient pas uniquement au terme utilisé : il ne s'agit pas uniquement d'utiliser moveTo au lieu de setPosition pour que la magie opère. il faut que moveTo mette tout ce qui est nécessaire en oeuvre pour s'assurer que l'objet déplacé puisse effectivement atteindre la position à laquelle on souhaite qu'il se rendre. Mais, comme la vérification sera différente pour un avion, pour un sous-marin et pour un véhicule, si c'est l'objet lui-même qui effectue la vérification, nous pouvons espérer que le développeur de la classe aura pris en compte toutes les conditions qui doivent être remplies ou qui peuvent empêcher un objet de type particulier d'atteindre une position d'un type donné. Et l'utilisateur de la classe n'a plus qu'à s'inquiéter d'interroger l'objet pour savoir s'il est en mesure d'atteindre la position indiquée

    Si tu as compris ce que j'essayais de te dire, tu comprendra assez facilement le reste :

    Les variables membres doivent être placées dans une accessibilités privée pour qu'il n'y ai jamais que les fonctions membres de la classe qui puissent y accéder (que ce soit pour les interroger ou pour les modifier). Les accessibilités publiques et protégées ne devraient contenir que des fonctions membres, qui seront placées dans l'accessibilité publique s'il s'agit de services que "monsieur tout le monde" doit pouvoir utiliser, dans l'accessibilité protégée si ce sont des services auxquels seules les classes dérivées doivent avoir accès.
    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.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. tableau de classe avec constructeur non copiable
    Par pixelou dans le forum Débuter
    Réponses: 3
    Dernier message: 23/02/2010, 00h09
  2. Système de classe, avec héritage multiple dynamique
    Par kimjoa dans le forum Contribuez
    Réponses: 0
    Dernier message: 09/01/2010, 19h07
  3. class avec constructeur déclarer en static
    Par tims12 dans le forum Langage
    Réponses: 4
    Dernier message: 29/12/2009, 18h58
  4. Héritage d'une classe avec constructeur privé
    Par Braillane dans le forum Langage
    Réponses: 13
    Dernier message: 02/09/2009, 12h59
  5. Réponses: 1
    Dernier message: 28/03/2006, 23h08

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