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 :

Que penser des frameworks et architectures qui font dériver toutes les classes d'un SuperObjet ?


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de poukill
    Profil pro
    Inscrit en
    Février 2006
    Messages
    2 155
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 2 155
    Par défaut Que penser des frameworks et architectures qui font dériver toutes les classes d'un SuperObjet ?
    Citation Envoyé par 3DArchi Voir le message
    P.S. : quand on a un projet où tous les objets dérivent de ObjetBase, c'est mal barré d'un point de vue conception.
    Ah ? Mais si tu veux que toutes tes classes implémentent quelque chose comme GetTypeName().
    Perso j'ai une classe IObject dont 99 % des classes de mon projet dérivent. Au début ça m'a fait bizarre, mais c'est très pratique. Bonne combinaison avec ma factory et tout ce qui va avec...
    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
     
    #ifndef OBJECT_H
    #define OBJECT_H
     
    #include <string>
     
    #include <boost/shared_ptr.hpp>
     
    #define DECLARE_TYPE(Class) \
    	virtual std::string GetTypeName() const {return #Class; }
     
    #define DECLARE_CLONE(Class) \
    	virtual boost::shared_ptr<IObject> Clone() const {return boost::shared_ptr<IObject>(new Class(*this)); }
     
     
     
    //-----------------------------------------------------------
    // IObject : Base class
    //-----------------------------------------------------------
     
    struct IObject
    {
    	IObject() {}
    	virtual ~IObject() {}
     
    	// Fonction to get the real type of the Class.
    	virtual std::string					GetTypeName	() const = 0;
     
    	// Polymorphic copy the object
    	virtual boost::shared_ptr<IObject>	Clone		() const
    	{
    		throw std::exception("This object is no clonable");
    	}
     
    };
     
    typedef boost::shared_ptr<IObject> object_ptr;
     
     
    #endif

  2. #2
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Ce qui serait étrange, c'est que toutes tes classes dérivent directement de ton IObject
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class MaClass : public IObject, (...)
    Par contre, qu'une classe dérive d'une classe qui dérive d'une classe qui dérive (...) qui dérive de IObject, ça ne pose pas de problème.

    Un exemple : dans Qt, la majorité des classes dérivent de QObject (pour pouvoir utiliser les meta infos, les signaux/slots, etc.)

  3. #3
    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 poukill Voir le message
    Ah ? Mais si tu veux que toutes tes classes implémentent quelque chose comme GetTypeName().
    Citation Envoyé par gbdivers Voir le message
    Ce qui serait étrange, c'est que toutes tes classes dérivent directement de ton IObject
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class MaClass : public IObject, (...)
    Par contre, qu'une classe dérive d'une classe qui dérive d'une classe qui dérive (...) qui dérive de IObject, ça ne pose pas de problème.

    Un exemple : dans Qt, la majorité des classes dérivent de QObject (pour pouvoir utiliser les meta infos, les signaux/slots, etc.)
    Beaucoup de framework ont des classes de ce genre : QObject (Qt)/wxObject (wxWidgets)/CObject (MFC). Mais, j'avoue que ça me laisse toujours dubitatif. Ca n'est pour moi pas très loin d'un void* d'un point de vue conception. Et comme tu le dis, la plus part du temps, ces objets n'ont qu'une ou deux fonctions de type info de classe ou clone. Elles ne peuvent avoir d'ailleurs pas vraiment d'autres fonctions car ils n'y a pas grand chose d'autre à factoriser. En plus tu obliges à de l'héritage et introduits des vtable là où il n'y en a peut être pas besoin.
    Je me demande si je ne préfèrerais pas passer alors par une classe trait pour définir et retrouver ces infos.

  4. #4
    Membre éclairé

    Profil pro
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Ce qui serait étrange, c'est que toutes tes classes dérivent directement de ton IObject
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class MaClass : public IObject, (...)
    Par contre, qu'une classe dérive d'une classe qui dérive d'une classe qui dérive (...) qui dérive de IObject, ça ne pose pas de problème.

    Un exemple : dans Qt, la majorité des classes dérivent de QObject (pour pouvoir utiliser les meta infos, les signaux/slots, etc.)
    Oui mais pas toutes pour garder de la souplesse, contrairement à Java et C# qui (si je ne m'abuse) héritent forcément de Object.

  5. #5
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Citation Envoyé par manudwarf Voir le message
    Oui mais pas toutes pour garder de la souplesse, contrairement à Java et C# qui (si je ne m'abuse) héritent forcément de Object.
    Non c'est juste, c'est implicite si on ne déclare rien. Toute classe est forcément un object, avec en gros en commun :

    toString()
    hashCode()
    equals()
    + un pseudo destructeur.

    Cette approche est utilisée pour les collections, les tables de hachage et assure une certaines cohérence dans le framework.

    Les frameworks de ces langages partent ainsi du principe que tout objet peut être clef d'une hashmap, toutes les méthodes de collection style remove(object) peuvent s'appuyer sur une méthode de test d'égalité et tout objet est représentable sous forme de string.

    Il faut aussi savoir que les generics ne fonctionnent pas comme les templates en C++ dans le sens ou ce n'est pas une sorte de processeur de texte (je sais que c'est réducteur).

  6. #6
    Membre chevronné
    Avatar de grishka
    Inscrit en
    Janvier 2003
    Messages
    285
    Détails du profil
    Informations forums :
    Inscription : Janvier 2003
    Messages : 285
    Par défaut
    Je pense qu'en général on privilégie l'héritage d'implémentation sans tenir compte des autres formes de réutilisabilité, notamment l'utilisation de la délégation et des interfaces (pour le polymorphisme) . Sans être "intégriste", l'héritage est souvent bien adapté à de petits ensembles de classe. Mais les grosses hiérarchies sont souvent difficiles à maintenir sur le long terme (A cause du fameux principe de substitution)

    certains frameworks applicatifs comme Spring ont grandement simplifiés la conception des applications. Ils permettent simplement de se passer de l'héritage (et d'autres design patterns comme le singleton, etc. ...) , en utilisant l'injection de dépendances et la configuration comme principes de base (bon ok, en simplifiant).

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Novembre 2009
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2009
    Messages : 69
    Par défaut
    Bonjour,
    [quote]
    Par contre pour le fonctionnement des signal/Slot de Qt, je pense toujours que sans le superObject se ne serait pas réellement possible. En particulier :
    * le principe d'appartenance à un thread
    * l'appel direct ou indirecte du slot suivant le cas inter/intra thread
    * les connexions dynamique
    Là oui, peut-être bien qu'une classe de base commune à tous les objets qui dialoguent est la seule solution.
    Je me permet de rebondir : tout ceci est complètement faisable sans héritage .
    Remarque/Solution 1 : pour implémenter de l'héritage, on fait de l'aggrégation. Donc c'est théoriquement possible de passer par de l'aggrégation.
    Solution 2 : on peut le simuler avec des templates.
    Solution 3 : on peut recopier le code sans passer par une classe de base.
    Il existe d'autres solutions, mais qui sortent un peu trop des sentiers battus pour que j'en discute ici.
    Quoiqu'il en soit, la solution 3 permet un gain de performance certain, tant au niveau de la vitesse d'exécution que de la vitesse de compilation (si je compare avec les templates).

    J'ai déjà eu l'occasion d'implémenter les versions 2 et 3. Je remarque que dans les entreprises, comme dans de nombreux projets open-source, la solution 3 est souvent négligée alors qu'elle n'est pas dénuée d'avantages.

    Le principe d'appartenance à un thread peut se traduire tout simplement par l'aggrégation d'un 'ID de thread'. Il suffit alors de comparer l'ID-thread de l'objet 'cible' exécutant le code en question avec celui du 'demandeur' pour déterminer si l'on souhaite exécuter la fonction de manière synchrone ou non (si ID1 == ID2 alors appel direct).

    A propos du SuperObject pour de l'instrospection :
    J'ai déjà implémenté un système d'introspection/réflexion en C++ utilisant la solution 3 sans difficultés. Cependant cela ne me sert qu'a faire des getters (nom attribs, nombre, taille, (de)serialization, scripting), pas de modification de type ni d'instanciation.

    SuperObject exemple de danger :
    J'ai constaté de sérieux problèmes dans un projet utilisant un SuperObject, dont l'un des premiers sous-type était un design pattern composite(!). Les problèmes étaient qu'une maison pouvait contenir une porte qui pouvait contenir une maison à nouveau etc... (ce qui est impossible) et le système ne détectait pas ces erreurs.

    Bref, de mon point de vue le SuperObject est plus dangereux qu'utile, même pour ceux qui n'ont pas de contraintes de performances.

    L'héritage est souvent bien adapté à de petits ensembles de classe
    Je partage aussi cet avis.

    Cordialement,
    El Pedro

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

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

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Par défaut
    Citation Envoyé par ElPedro Voir le message
    Remarque/Solution 1 : pour implémenter de l'héritage, on fait de l'aggrégation. Donc c'est théoriquement possible de passer par de l'aggrégation.
    Solution 2 : on peut le simuler avec des templates.
    Solution 3 : on peut recopier le code sans passer par une classe de base.
    1 : Ca ne permet pas d'avoir un type unique à mettre dans un tableau par exemple.

    2 : A bas niveau, je veux bien; A haut niveau, ça revient à faire de l'héritage sans le documenter. Dans tous les cas, ça impose les mêmes contraintes sur la classe dérivée, sauf que l'on perd beaucoup d'information que l'on trouve par exemple dans des classes composées de méthodes virtuelles pure.

    3 : J'espère que c'est pas une incitation au copier/coller? Sinon, après les patterns, les anti-patterns : D.R.Y.

    SuperObject exemple de danger :
    J'ai constaté de sérieux problèmes dans un projet utilisant un SuperObject, dont l'un des premiers sous-type était un design pattern composite(!). Les problèmes étaient qu'une maison pouvait contenir une porte qui pouvait contenir une maison à nouveau etc... (ce qui est impossible) et le système ne détectait pas ces erreurs.
    C'est un exemple de mauvaise utilisation de la généricité et à la rigueur d'absence de contrôle sur les types génériques. C'est le modèle métier maison qui ne fait pas les vérifications nécessaires.
    Pas besoin de SuperObjet pour créer ce genre de problèmes. Il suffit d'un mauvais modèle.


    Bref, de mon point de vue le SuperObject est plus dangereux qu'utile, même pour ceux qui n'ont pas de contraintes de performances.
    Etendre le modèle Objet de C++ à l'aide de C++, me semble beaucoup moins dangereux que la présence de pointeur dans ce langage et l'obligation de passer par ses derniers dans certains cas de polymorphisme.

    Pour l'utilité : Aucune chance d'avoir un moteur de script tel celui de Qt sans le QObjet pour l'introspection sur les slots, signaux et propriétés. Regarde la lourdeur du code à mettre en place pour exposer une classe en javascript dans v8 de google (à base de template), par rapport à QtScript.


    L'héritage est souvent bien adapté à de petits ensembles de classe
    L'héritage est adapté aux comportements identiques point barre. Dans le cas de Qt, tout QObjet est potentiellement liés par des signaux à d'autres objets. Il n'y a pas lieux de cacher cet héritage.

    En C++, on hérite d'une table de méthode virtuelles en natif après tout. En Java, on ne se plaint pas d'avoir un si pratique Objet sous-jasent à toutes les classes, ainsi que Class qui permet d'avoir un ClassLoader. On comprend encore plus son intérêt quand on écrit une JNI et que l'on retrouve un JObjet.

    Il faut bien avoir à l'esprit ce qui fait que l'on préfère la composition à l'héritage, pour ne pas appliquer mécaniquement ce principe. Quand le SuperObjet rend les choses plus modulable et plus interchangeable; il peut apporter plus d'avantage que d'inconvénients.

    Le fait est que le modèle Objet de C++ est restreint en natif au strict nécessaire. Dès lors que l'on veut l'utiliser en plus haut niveau, que l'on veut faire communiquer ces objets, on est tenté d'étendre le modèle objet natif de C++.

    Unifier le nouveau modèle objet à travers un super-objet, plutôt que de le sous-entendre dans des templates ou autres, n'est pas à mon sens une aberration.

  9. #9
    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 jblecanard Voir le message
    Pas du tout. Comme le disent d'autres rédacteurs, c'est beaucoup utilisé dans de gros projets, car celà permet par exemple d'ajouter une gestion du cycle de vie des objets plus fine, de tracer leur existence en mode dev, etc...
    Que des fonctions qui n'ont rien à voir avec un héritage public...
    Je maintiens que faire dériver publiquement tous ses objets d'un MyGodObject est une erreur de conception ou, disons, une facilité de conception qui dans 99,9% des cas peut être abordée autrement.

  10. #10
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Que des fonctions qui n'ont rien à voir avec un héritage public...
    Pas exactement. Si tu veux gérer un compteur de référence par exemple, en appelant une méthode quand tu ajoute une référence et une autre pour la libérer, il faut que ton héritage soit public.

    Après, si ta conception est "plate" et que tous les objets héritent tous directement du même parent, là d'accord il y a quelque chose qui ne va pas. (edit clarté)

  11. #11
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Un exemple de d'héritage public : dans Qt, faire dériver la majorité des objets de QObject permet de profiter les méta infos et des signaux/slots par exemple.
    une facilité de conception qui dans 99,9% des cas peut être abordée autrement
    Je suis d'accord qu'il s'agit d'une facilité et qu'il ne faut pas l'utiliser quand c'est pas nécessaire (et qu'il faut se poser la question, si on utilise ce type d'approche, de savoir s'il n'y a pas un problème de conception).

    Mais dans un framework (Qt, wx, MFC que tu citais), ce système permet quand même une grande souplesse et facilite son utilisation, ce qui n'est pas négligeable pour que le framework soit plus largement utilisé (les utilisateurs ne veulent pas forcement passer des semaines à lire la doc pour pouvoir utiliser le framework).

    Par exemple, pour les métas infos et les signaux/slots de Qt, comment aurais-tu fait à la place sans hériter d'un QObject arrière-arrière-arrière-...-arrière-grand-parent ?
    (ma question n'est pas totalement désintéressée je travaille actuellement sur une nouvelle version d'un framework pour le passer d'une version 1 qui utilise beaucoup les template mais qui est particulièrement complexe à utiliser, à une version 2 qui utilisera ce système. J'espère beaucoup gagner en facilité d'utilisation pour un cout en temps faible)

Discussions similaires

  1. Réponses: 36
    Dernier message: 12/01/2011, 15h55
  2. Que penser des testeurs de Carte Mère
    Par Fabdeuche dans le forum Ordinateurs
    Réponses: 1
    Dernier message: 26/11/2010, 10h35
  3. Réponses: 0
    Dernier message: 15/11/2010, 11h51
  4. Un programme qui lance quelquechose toute les 50 minutes?
    Par altadeos dans le forum C++Builder
    Réponses: 4
    Dernier message: 12/03/2006, 11h16

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