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 :

Pointeurs / Références / Héritages -> besoin de précisions


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 162
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 162
    Billets dans le blog
    153
    Par défaut Pointeurs / Références / Héritages -> besoin de précisions
    Bonjour à tous,

    Comme vous pouvez le deviner, j'ai une question sur le C++. Notamment sur les pointeurs / références et héritages.
    Un moment, j'ai cru qu'il fallait toujours utiliser les pointeurs pour utiliser les classes que nous héritions. Ainsi, nous pouvions faire une liste de pointeur sur la classe mère qui entrainait l'appel à la fonction hérité des sous classes, si disponible, selon le vrai type "qui se cache derrière le pointeur générique" (si je puis dire).

    Hum, je ne sais pas trop comment énoncé la question et en fait, je ne suis même pas sur d'avoir une question sur ce point. Bref passons (certes, c'est encore un peu le fouillis dans ma tête).

    J'énonce souvent une règle (que je puisse avoir entendu sur ce forum) qui est d'éviter à tout pris les pointeurs (entre autre, car c'est nous qui gérons la durée de vie des objets). Le mieux étant d'utiliser des smart pointeurs comme ceux de boost.
    Mais, je voulais savoir, lorsque nous avons une classe qui contient une instance d'une autre classe. Par exemple, une classe Renderer qui contiendrai une Camera.
    Si en dehors de ma classe Renderer, j'ai besoin de modifier la camera, faut il mieux que je fasse un setter qui retourne un pointeur ou une référence? (Dans les deux cas, je ne retourne pas quelque chose de constant ... ce qui m'embête un peu).
    En fait, si vraiment la classe Renderer devait garder le controle complet de la camera, il ne faudrait même pas que je fasse de setter, mais qu'à la place je réimplémente des fonctions, qui appellerai celle de la classe camera. N'y a t'il pas un design pattern pour me simplifier ce processus?

    Finalement, si j'utilise un std::vector<T> de mon objet. Dois-je stocker des pointeurs ou dois-je essayer de faire sans les pointeurs ?
    Il me semble que le seul cas ou je suis vraiment forcé d'utiliser les pointeurs, ces lorsque la classe que je veux mettre dans mon vecteur est abstraite pure, ou du moins, lorsque c'est une classe mère et que je veux une liste de ceux-ci pour appeler les méthodes hérités facilement. Ai-je juste?

    Merci de m'éclairer sur ces points qui me mettent souvent en doute.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,

    Citation Envoyé par LittleWhite Voir le message
    Un moment, j'ai cru qu'il fallait toujours utiliser les pointeurs pour utiliser les classes que nous héritions. Ainsi, nous pouvions faire une liste de pointeur sur la classe mère qui entrainait l'appel à la fonction hérité des sous classes, si disponible, selon le vrai type "qui se cache derrière le pointeur générique" (si je puis dire).

    Hum, je ne sais pas trop comment énoncé la question et en fait, je ne suis même pas sur d'avoir une question sur ce point. Bref passons (certes, c'est encore un peu le fouillis dans ma tête).
    Deux points :

    L'héritage suppose une sémantique d'entité et une sémantique d'entité est incompatible avec la copie. Donc, un objet d'une telle classe, étant non copiable, est naturellement manipulé par pointeur ou par référence lorsqu'il traverse des fonctions ou d'autres objets.

    Ensuite, très concrètement, avec un typage statique comme le C++, pour utiliser le polymorphisme d'inclusion, une expression doit utiliser une variable dont le type dynamique et le type statique peuvent différer (Type statique et type dynamique). En C++, seul les pointeurs et les références peuvent avoir un type dynamique différent du type statique.

    Citation Envoyé par LittleWhite Voir le message
    J'énonce souvent une règle (que je puisse avoir entendu sur ce forum) qui est d'éviter à tout pris les pointeurs (entre autre, car c'est nous qui gérons la durée de vie des objets). Le mieux étant d'utiliser des smart pointeurs comme ceux de boost.
    Je ne comprends pas la règle comme étant il faut éviter à tout prix les pointeurs. Mais plutôt :
    1/ Une classe doit avoir une responsabilité unique (SRP).
    2/ Gérer un pointeur, c'est compliqué ( robustesse face aux exceptions, politique de partage, détruire au bon moment, etc.) : c'est une responsabilité à part entière.
    Conclusion :
    Une classe qui doit gérer une ressource pointeur doit déléguer cette responsabilité à une classe dédiée (un pointeur intelligent, un std::vector, etc) sans quoi elle serait amené à multiplier les responsabilités.

    En revanche, un shared_ptr est souvent dégainé avec une grande facilité, pour ne pas dire par paresse, parce qu'on a fait l'économie en phase design de définir le rôle exact lié au pointeur (simple association, agrégation ou composition).

    Citation Envoyé par LittleWhite Voir le message
    Mais, je voulais savoir, lorsque nous avons une classe qui contient une instance d'une autre classe. Par exemple, une classe Renderer qui contiendrai une Camera.
    La camera est-elle un membre de Renderer ou est-elle un paramètre d'une fonction d'exécution du rendu de Renderer ? Un indicateur de cohérence d'une classe consiste à mesurer l'utilisation d'un membre au regard de l'ensemble des fonctions publiques de la classe. Plus cette fréquence est élevée, plus le membre est pertinent. A l'inverse, si le membre n'est utilisé que dans une fonction, probablement est-il plus pertinent que ce soit un paramètre.

    S'il s'agit d'un membre de la classe, reste à identifier :
    1/ Les durées de vie du conteneur et du contenu sont elles fortement liées (composition) ? le contenu peut-il être partagé entre différents conteneurs (aggrégation) ? le lien contenu/conteneur est-il encore plus lâche (association) ?
    2/ Le contenu a-t-il une sémantique d'entité ou de valeur ?
    3/ Comment est créé l'objet membre (dependance injection) ?

    Citation Envoyé par LittleWhite Voir le message
    Si en dehors de ma classe Renderer, j'ai besoin de modifier la camera, faut il mieux que je fasse un setter qui retourne un pointeur ou une référence? (Dans les deux cas, je ne retourne pas quelque chose de constant ... ce qui m'embête un peu).
    En fait, si vraiment la classe Renderer devait garder le controle complet de la camera, il ne faudrait même pas que je fasse de setter, mais qu'à la place je réimplémente des fonctions, qui appellerai celle de la classe camera. N'y a t'il pas un design pattern pour me simplifier ce processus?
    Un design pattern, non. Mais qui dit lien entre deux objets, ne dit pas forcément composition ou agrégation. L'association suffit peut être. Au quel cas, Renderer et Camera ne sont pas aussi fortement couplés et permet que Caméra et XXX soit aussi couplés sans passer par Renderer.

    Un setter est une mauvaise idée (ne serait-ce que forcément tu vas violer la pauvre Démeter), retourner un pointeur/référence non constant sur un membre a raison de te déranger (c'est d'ailleurs souvent interdit dans les règles de codage)

    Citation Envoyé par LittleWhite Voir le message
    Finalement, si j'utilise un std::vector<T> de mon objet. Dois-je stocker des pointeurs ou dois-je essayer de faire sans les pointeurs ?
    Il me semble que le seul cas ou je suis vraiment forcé d'utiliser les pointeurs, ces lorsque la classe que je veux mettre dans mon vecteur est abstraite pure, ou du moins, lorsque c'est une classe mère et que je veux une liste de ceux-ci pour appeler les méthodes hérités facilement. Ai-je juste?
    Pointeur ou référence vont être le moyen naturel/obligatoire du langage pour répondre à un besoin de conception : sémantique d'entité (donc héritage que la classe de base soit abstraite ou pas), association/agrégation, dissociation construction membre/objet, etc.

  3. #3
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 28
    Par défaut
    Intéressante question qui me préoccupe aussi. Merci 3DArchi pour ta réponse précise.

    Pour être sur de bien différencier les 3 designs :
    Citation Envoyé par 3DArchi Voir le message
    simple association, agrégation ou composition
    Est-ce que c'est bien cela ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    //composition
    class B : A {
    }:
     
    //agrégation
    class B {
    A member;
    };
     
    //association
    class B {
    void function (const A& a);
    };
    Dans le cas de la caméra et du renderer. Je dirais qu'un renderer peut avoir plusieurs caméras (par exemple un soft 3D a différentes vues de face, côté etc...) et je me dirigerais vers l'association (cf. ma vision de l'association plus haut).

  4. #4
    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:

    Aggrégation :
    relation "contenant à contenu" qui existe entre deux classes dans laquelle un contenu peut être lié à plusieurs contenant.

    Typiquement, nous serions proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Contenant
    {
        private:
            Contenu * ctn;
            // voir
            Collection < Contenu *> collection; // Collection: std::vector, par ex
    };
    Composition :
    Relation "contenant à contenu" qui existe entre deux classes dans laquelle le contenu ne peut pas subsister si le contenant est détruit.

    Typiquement, cela se représente sous la forme de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Contenant
    {
        private:
            Contenu  ctn;
            // voir
            Collection < Contenu > collection; // Collection: std::vector, par ex
    };
    Enfin, l'association permet "simplement" de faire "travailler de concert" deux types distincts, parce que l'un (celui au départ duquel on invoque une fonction / un comportement) a "besoin" du comportement offert par l'autre, ou parce que le premier "prend la responsabilité" d'invoquer un certains nombre de comportements du deuxième selon une logique clairement définie.

    Si l'objet utilisé dans la fonction est susceptible d'être différent en fonction des différents endroits où la fonction est appelée, cela se traduira souvent par le passage de l'objet sous la forme d'un argument

    L'héritage quant à lui implémente une relation de généralisation / de spécialisation.

    Soit parce que l'on peut considérer qu'un objet du type dérivé EST UN objet du type de base dont certains comportements sont adaptés au type dérivé (fonctions virtuelles), comme c'est le cas pour l'héritage publique (tu peux alors décider d'utiliser n'importe quel objet du type dérivé lorsque tu t'attends à manipuler un objet du type de base), soit tu considère que ton type dérivé agit en interne "un peu comme" le type de base, mais que l'interface du type de base n'est pas "tout à fait adapté" aux besoin du type dérivé.

    Nous sommes là à un niveau de dépenance bien plus important que les différentes relations dont j'ai parlé plus haut
    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

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par arthurG Voir le message
    Est-ce que c'est bien cela ?
    Pas tout à fait. Comme le dit Koala, composition, agrégation et association sont trois intensité de lien différents entre 2 objets allant du plus engageant (la composition) au moins engageant (l'association). L'héritage privé peut être utilisé pour la composition mais ce n'est pas toujours pertinent. Un membre pointeur ou par valeur peut aussi être utilisé (si nécessité d'allouer sur le tas, d'utiliser du polymorphisme, d'avoir une création non présente dans la classe, etc.). Pour l'agrégation ou l'association, il est vrai que ce sera plus probablement un pointeur puisque les durées de vie (et à travers ça le couplage) sont sensées être disjointes.


    Citation Envoyé par bretus Voir le message
    Que pensez vous de cette approche?
    C'est une très intéressant réponse. J'essaie de trouver le temps de te répondre plus longuement dans le w.e. en particulier :
    - sur les aspects getter/setter qui reviennent à poser la question de la définition d'une classe et pourquoi l'amitié peut alors avoir un sens (sérialisation, builder)
    - sur les aspects Demeter : ou quand une rupture de style ou d'un loi (tout comme son artificialité dans son utilisation) reflète plus un problème d'architecture et de conception d'abord.
    - sur les aspects évolutions et reuse
    Mais je sens à ta réponse que nous somme globalement d'accord sur le pelage du troll

  6. #6
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Juin 2011
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juin 2011
    Messages : 28
    Par défaut
    Merci pour les précisions koala01 et 3DArchi. Je me fourvoyais complètement.
    C'est le genre de threads utiles, j'attends la suite avec impatience

  7. #7
    Membre émérite
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    553
    Détails du profil
    Informations personnelles :
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2009
    Messages : 553
    Par défaut
    Bonjour,

    Citation Envoyé par 3DArchi Voir le message
    Un setter est une mauvaise idée (ne serait-ce que forcément tu vas violer la pauvre Démeter), retourner un pointeur/référence non constant sur un membre a raison de te déranger (c'est d'ailleurs souvent interdit dans les règles de codage)
    En même temps, si la loi de Démeter protège le concepteur de la classe de l'indiscrétion de ses utilisateurs, elle contraint surtout l'utilisateur à être intrusif pour procéder à des ajouts de fonctionnalités. A mon sens, son application aveugle, en particulier sur des briques de base, induit une trop grande suspicion sur l'utilisation des classes du système : Rien de bon en terme de ré-utilisabilité.


    Plutôt que d'appliquer bestialement des règles, il vaut mieux comprendre ce qu'implique le non respect de ces règles. Je vais essayer de préciser les implications du non respect de règles, vous m'éclaircirez sûrement si je me fourvoies (en trollant, on progresse plus qu'en se cachant ).

    Qu'implique l'accès publique à un membre (le mal absolu)?

    Si on donne l'accès publique à un membre "x" sur une classe "Point", on se prive de tout refactoring vers une autre représentation interne.

    On se prive de pouvoir passer de :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class Point {
    public:
    	double x;
    	double y;
    };
    à

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class Point {
    public:
    	Coordinate coordinate;
    };
    Autant dire que ça n'est pas très souple

    Qu'implique l'accès en référence const et non const à un membre (intermédiaire)

    A mon sens, l'accès par référence non constante est un compromis entre l'accès public à un membre et l'accès par un setter classique.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    class Point {
    public:
     
    	inline const double & x() const { return _x; }
    	inline double &       x() { return _x; }	
    	inline const double & y() const { return _y; }
    	inline double &       y() { return _y; }
     
    private:
    	double _x;
    	double _y;
    };
    On peut :
    - modifier la représentation interne (passer _x,_y en _coordinate)

    On ne peut pas :
    - mettre par la suite une logique lors de la modification (par exemple, pré-calculer la norme lors de setX)
    - contrôler la cohérence interne de la représentation (A prohiber sur un interval min,max par exemple)

    Pour les adeptes des langages objets où tout est référence et où la notion de const est absente, cette problématique revient à la question : Mon getter doit-il renvoyer un clone ou pas? (Tous les getters Java ne renvoyant pas de clone sont équivalents à l'approche du const/non const)

    Qu'implique l'accès en getter constant (readonly) et setter

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    class Point {
    public:
     
    	const double & getX() const { return _x; }
    	void 		   setX( const double & x ) { _x = x; }
     
    	const double & getY() const { return _y; }
    	void 		   setY( const double & y ) { _y = y; }
     
    private:
    	double _x;
    	double _y;
    };
    On peut :
    - changer la représentation interne

    On ne peut pas :
    - Modifier sans remplacer (sans procéder à des copies)

    Est-ce donc la panacée? Ceux qui laissent l'accès en modification sont-ils des fous furieux : je ne pense pas. S'il y a aucune raison valable de protéger l'accès en modification : Autant le fournir pour simplifier la vie des utilisateurs.


    Pourquoi Demeter? A mon humble avis, parce-que le problème est souvent mal posé avec une approche purement locale des relations entre classes : Le cas du Renderer référençant la Camera est typique. On ne veut pas à juste titre que l'utilisateur fasse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    renderer.getCamera().setPosition( position );
    Mais le code ci-après sera tout aussi paumant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::auto_ptr< Camera > camera( renderer.getCamera().clone() );
    renderer.setCamera( camera.release() );
    L'origine du mal : La relation directe entre le Renderer et une classe Camera n'a pas raison d'être, on cherche donc à tout prix à la masquer aux utilisateurs de la classe Renderer et on la virera probablement en cas de refactoring pour du multi-camera comme l'indique arthurG.


    <troll>Demeter dans son approche interdisant le chaînage est un cache misère en cas de relation de voisinage direct qui n'ont pas lieu d'être</troll>

    A mon humble avis, il existe une approche plus viable : Centraliser l'accès aux éléments de sorte que le parcours des liens aient une signification. Cette approche revient à limiter le nombre de voisin direct aux seuls éléments qui composent la classe et à un point d'accès vers les principales classes du système (les fabriques, les paramètres, le système de log, etc...)


    En guise d'exemple, dans le cas d'un système de rendu, on pourrait introduire la notion de "monde" fournissant l'accès aux différents éléments du système de rendu. Chaque élément du système de rendu à accès à World (sans l'exposer). Tous les éléments du système sont accessibles en descendant les relations à partir de World.

    Que devient l'accès aux composants du système :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    World world;
    //acces en modification a la camera
    world.getCamera().setPosition( Point(4.0,6.0) );
    //acces en remplacement de la camera (fabrique pour passer world au constructeur)
    CameraFactory & cameraFactory = world.getCameraFactory();
    world..setCamera( cameraFactory.newTrackingCamera(idObjet) );
    Que devient le renderer (sans singleton, en passant sous silence une éventuelle fabrique de Renderer):

    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 Renderer : public WorldEntity {
    public:	
    	Renderer( World & world ):
    		WorldEntity(world)
    	{
    	}
     
     
    	void render() {
    		Camera & camera = world().getCamera();
    		//rendering
    	}
     
    };

    Qu'est-ce que l'on perd en possibilité de refactoring : Pas grand chose voir rien :
    - Si on déporte la caméra sur un Viewer, il suffira de transformer l'accès à la camera dans la classe World
    - Si on met en place un système multi-caméra, il suffira de renvoyer la caméra active dans getCamera.

    On y gagne même beaucoup au final car les voisins direct que l'on masquait avec demeter disparaissent.


    En somme, plutôt que de s'obstiner à priver l'utilisateur du chaînage, ne vaut-il pas mieux le guider dans un chaînage intelligent partant d'un point d'accès central (ci-avant World) en fournissant des relations qui aient un sens?

    Que pensez vous de cette approche?

  8. #8
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par bretus Voir le message
    Qu'implique l'accès publique à un membre (le mal absolu)?
    Je ne suis déjà pas d'accord pour nommer ça le mal absolu
    Citation Envoyé par bretus Voir le message
    Si on donne l'accès publique à un membre "x" sur une classe "Point", on se prive de tout refactoring vers une autre représentation interne.
    [snip exemple]
    Autant dire que ça n'est pas très souple
    C'est possible, mais est-ce qu'on a vraiment besoin de cette souplesse ? Surtout si elle a un coût ?
    Déjà, il existe des classes qui ne sont que des aggrégations de données, sans vraiment un comportement associé (sauf la notion d'une certaine cohérence à la construction). Pour de telles classes, je n'hésite pas à mettre tous mes membres en public (j'écris d'ailleurs souvent une telle classe 'struct'). Ce qui est très rare dans mon code est d'avoir un mélange d'accès différents pour différentes données membres d'une même classe.

    L'exemple académique du point en (x,y) que l'on pourrait vouloir basculer en (arg,mod) me semble un peu artificiel (même si je l'utilise dans mes cours, il est simple à comprendre et expliquer). Pourquoi ? Parce que l'interface d'une classe, ce ne sont pas seulement les prototypes de ses fonctions publiques et de ses fonctions virtuelles, mais c'est aussi un tas de choses, dont les performances.

    Si appeler une fonction getX passe de rien du tout à deux calculs de trigonométrie, les utilisateur verront que le code a changé, même s'il conserve la même interface. Je ne pense pas qu'une bibliothèque puisse faire ce genre de modifications sans l'accord de ses clients, qui vont alors devoir eux aussi modifier du code. On n'est pas vraiment dans la pure théorie du découplage entre code.

    Un autre coût, plus sournois (mais là, de nombreuses personnes sont en désaccord avec moi), c'est que le C++ ne fourni pas d'outils pour rendre l'utilisation de fonctions aussi agréable que l'utilisation de données membre. Je préfère largement dire
    result.R = (data.R + data.G + data.B) / 3;que result.setR((data.R() + data.G() + data.B())/3);.


    Toutes ces parenthèses, nuisent à la lisibilité, et sont source de bugs et de perte de temps.

    Certains vont me répondre que la bonne solution est d'avoir du code result = unAlgorithmeIntelligent(data). Dans certains cas, je suis d'accord. C'est plus réutilisable, documentée et tout. Mais il y a des domaines où les combinatoires des données membre existent à l'infini, et ne sont quasiment pas répétées. Et ou finalement fournir les centaines d'algorithmes spécifiques à chaque fois, on n'améliore pas la lisibilité, et on repousse juste le problème d'un cran.
    Citation Envoyé par bretus Voir le message
    Qu'implique l'accès en référence const et non const à un membre (intermédiaire)
    [snip]
    On peut :
    - modifier la représentation interne (passer _x,_y en _coordinate)
    Pas vraiment, même si un wrapper peut permettre de faire semblant dans certains cas, au prix d'une certaine lourdeur
    Citation Envoyé par bretus Voir le message
    On ne peut pas :
    - mettre par la suite une logique lors de la modification (par exemple, pré-calculer la norme lors de setX)
    On peux, toujours avec un wrapper, toujours aussi lourd
    Citation Envoyé par bretus Voir le message
    - contrôler la cohérence interne de la représentation (A prohiber sur un interval min,max par exemple)
    Idem
    Citation Envoyé par bretus Voir le message
    Qu'implique l'accès en getter constant (readonly) et setter
    [snip]
    On peut :
    - changer la représentation interne
    Voir mes remarques du point 1
    Citation Envoyé par bretus Voir le message
    En guise d'exemple, dans le cas d'un système de rendu, on pourrait introduire la notion de "monde" fournissant l'accès aux différents éléments du système de rendu. Chaque élément du système de rendu à accès à World (sans l'exposer). Tous les éléments du système sont accessibles en descendant les relations à partir de World.
    Je n'aime pas trop cette notion de god object...
    Citation Envoyé par bretus Voir le message
    Qu'est-ce que l'on perd en possibilité de refactoring : Pas grand chose voir rien :
    - Si on déporte la caméra sur un Viewer, il suffira de transformer l'accès à la camera dans la classe World
    - Si on met en place un système multi-caméra, il suffira de renvoyer la caméra active dans getCamera.
    Là, tu supposes qu'il n'y a qu'une caméra active. On peut envisager au moins une caméra par renderer, voir plusieurs (dans la scène affichée, on affiche un écran qui lui même affiche la scène selon un autre angle, voir par exemple le jeu Portal qui joue à fond sur ce principe)
    Citation Envoyé par bretus Voir le message

    En somme, plutôt que de s'obstiner à priver l'utilisateur du chaînage, ne vaut-il pas mieux le guider dans un chaînage intelligent partant d'un point d'accès central (ci-avant World) en fournissant des relations qui aient un sens?

    Que pensez vous de cette approche?
    En l'occurrence, je préfère à priori la solution proposée plus tard qui propose de passer la caméra en argument du renderer.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  9. #9
    Membre émérite
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    553
    Détails du profil
    Informations personnelles :
    Localisation : France

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

    Informations forums :
    Inscription : Mars 2009
    Messages : 553
    Par défaut
    Bonsoir,

    Citation Envoyé par JolyLoic Voir le message
    Je ne suis déjà pas d'accord pour nommer ça le mal absolu
    C'est possible, mais est-ce qu'on a vraiment besoin de cette souplesse ? Surtout si elle a un coût ?
    Excès de prudence peut-être.

    Citation Envoyé par JolyLoic Voir le message
    L'exemple académique du point en (x,y) que l'on pourrait vouloir basculer en (arg,mod) me semble un peu artificiel (même si je l'utilise dans mes cours, il est simple à comprendre et expliquer). Pourquoi ? Parce que l'interface d'une classe, ce ne sont pas seulement les prototypes de ses fonctions publiques et de ses fonctions virtuelles, mais c'est aussi un tas de choses, dont les performances.
    Juste remarque.

    Citation Envoyé par JolyLoic Voir le message
    Un autre coût, plus sournois (mais là, de nombreuses personnes sont en désaccord avec moi), c'est que le C++ ne fourni pas d'outils pour rendre l'utilisation de fonctions aussi agréable que l'utilisation de données membre. Je préfère largement dire
    result.R = (data.R + data.G + data.B) / 3;que result.setR((data.R() + data.G() + data.B())/3);.
    Si tu parles des notions de property ou des getter/setter "magique", je pense que leur présence rendrait moins parano en matière de possibilité de refactoring.
    En leurs absences, le passage de public à get/set génère des changements cassants.

    Citation Envoyé par JolyLoic Voir le message
    [...]Je n'aime pas trop cette notion de god object...Là, tu supposes qu'il n'y a qu'une caméra active. On peut envisager au moins une caméra par renderer, voir plusieurs (dans la scène affichée, on affiche un écran qui lui même affiche la scène selon un autre angle, voir par exemple le jeu Portal qui joue à fond sur ce principe)
    En l'occurrence, je préfère à priori la solution proposée plus tard qui propose de passer la caméra en argument du renderer.
    Ca se vaut je pense, sauf que tu préfères un couplage faible entre le renderer et ce qui l'appel... Dans l'approche World, le renderer peut récupérer l'ensemble des caméras actives. Ca oblige l'appelant du renderer à connaitre les besoins du renderer (caméra et autre) mais ça se défend

    A propos du "god-object", sans insister lourdement , LittleWhite ressent toutefois le besoin de Scene pour gérer la durée de la caméra.

    ++,
    merci de vos précisions

  10. #10
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 162
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 162
    Billets dans le blog
    153
    Par défaut
    Citation Envoyé par bretus Voir le message
    A propos du "god-object", sans insister lourdement , LittleWhite ressent toutefois le besoin de Scene pour gérer la durée de la caméra.
    Oui, mais ce n'est pas comparable au "god-object" (enfin je crois), d'une part, car un moteur 3D à souvent (si ce n'est pas tout le temps) la notion de scène, qui contient les géométrie, les lumières, les matériaux, les cameras.
    Alors que dans le cas d'un "god-object" dans un moteur 3D, on se retrouverai avec les shaders, les scènes, les textures et tout ce que l'on peut imaginer d'utile.
    Après, le besoin d'une scène et d'une part à cause que je n'en ai pas mais aussi car la caméra ne peut pas être couplé aussi lourdement avec le renderer (cela à peut de sens disons). Donc il faut bien une autre classe

    EDIT:
    Surtout que j'ai oublié de dire, si "god-object" celui ci gérerai la durée de vie de tout les objets. Si destruction de celui-ci, il détruira Scène + Renderer.
    Alors que en fait, une Scene, peut être détruite et que l'on garderai le Renderer (ainsi que les texture / shaders chargés). Donc la scène n'est pas un "god-object" à proprement parler
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

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

Discussions similaires

  1. Pointeur et héritage...
    Par Zenol dans le forum C++
    Réponses: 2
    Dernier message: 08/03/2006, 12h31
  2. Passge par référence & héritage
    Par jsatla dans le forum C++
    Réponses: 11
    Dernier message: 04/01/2006, 17h44
  3. [VB.NET]Besoin de précision pour architecture 3-tiers
    Par Dnx dans le forum Windows Forms
    Réponses: 8
    Dernier message: 14/09/2005, 09h09
  4. Besoin de précision sur TThread
    Par psau dans le forum C++Builder
    Réponses: 2
    Dernier message: 15/02/2005, 23h35
  5. Réponses: 8
    Dernier message: 26/08/2004, 18h59

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