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

Contribuez C++ Discussion :

Le langage D


Sujet :

Contribuez C++

  1. #201
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Alp Voir le message
    Au fait, si on fait une version constexpr de sin, on peut très bien faire ça en C++0x hein ponce.
    Oui, bientôt sans doute

  2. #202
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Les propriétés n'étant, comme tu le fais remarquer, qu'un "sucre syntaxique" remplaçant les mutateur et accesseurs, tu est tenté de déclarer "propriété" (avec les accesseurs et mutateurs que le terme induit) quelque chose qui... ne devrait pas l'être...
    Pour ma part, je vois surtout les propriétés comme une manière propre et élégante d'avoir des attributs vus comme des variables sans devoir les déclarer publics et/ou risquer l'incohérence en cas "d'oubli" d'appel d'un accesseur... Cela permet également d'avoir des attributs réellement write/read only, et cela centralise à l'endroit où l'on définit la propriété sa valeur par défaut (au lieu d'en avoir un bout dans le constructeur), bref c'est nettement plus propre et lisible. Tout comme le With du Pascal continue de me manquer sévèrement en C/C++ lors de la manipulation de structures / classes un peu complexes, même après plus de 10 ans de C/C++ intensif...

    Après, une "vraie" propriété est normalement très contrainte au niveau des prototypes, on ne peut donc pas mettre n'importe quoi dessus. Mais c'est extrêmement agréable de pouvoir utiliser les "attributs" directement, comme des variables, en se contrefichant de savoir si oui ou non il y a une fonction à appeler derrière. En cas d'évolution (plus ou moins de contrôles requis par exemple, verrouillage d'un sens d'accès, changement d'implémentation), la syntaxe dans le programme appelant reste la même et, surtout, elle reste bien plus lisible qu'avec trois tonnes de fonctions s'appellant les unes les autres...

    Entre un Object1.Attribute1 += Object2.Attribute2 ; et un Object1.SetAttribute1(Object1.GetAttribute1()+Object2.GetAttribute2()) ;, perso, je sais quelle écriture est la plus lisible...

    Pour ma part, quand j'ai la possibilité d'utiliser des propriétés, je déclare directement les propriétés correspondant aux attributs... Quitte à les laisser en read-only.

    Certes, je suis "marqué" par les propriétés sous Delphi, mais elles ont été implémentées également sous BCB, de la même façon, et ça n'a pas trop l'air de poser problème jusqu'à présent.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  3. #203
    Invité
    Invité(e)
    Par défaut
    Super, c'est exactement pour toutes ces raison que j'aime bien les propriétés.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Le fait est que, en étant à peine un peu exhaustif, tu ne devrais pas voir à écrire un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object1.Attribute1 += Object2.Attribute2 ;
    ni un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object1.SetAttribute1(Object1.GetAttribute1()+Object2.GetAttribute2()) ;
    En effet, ces codes devraient, en toute bonne logique, être intégrés dans un comportement que l'on souhaite autoriser (et qui doit donc être pris en compte au moment de la conception) pour la classe en question.

    Cela pourrait, pour le premier, prendre la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MaClass::Add(Maclass const & rhs)
    {
        Attribute1+=rhs.Attribute1;
    }
    et, pour le second etre proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MaClass::synchronize(MaClass const & rhs)
    {
        Attribute1=rhs.Attribute1;
    }
    car les approches que je présente permettent de *réellement* encapsuler Attribute1 et de respecter demeter.

    J'ai, encore une fois, l'impression que vous avez des approches, si pas fragmentaires, en tout cas restrictives ou erronées de ce que peuvent représenter l'encapsulation, la loi demeter, ou le concept orienté objet lui-même.

    J'admets que, dans certains cas, un accesseur puisse s'avérer intéressant, mais, très souvent, il subira l'énorme inconvénient de fournir un acces "global" (dans le sens de disponible dans n'importe quelle portion de code) à quelque chose dont l'acces devrait être particulièrement restreint.

    Et, comme de bien entendu, j'émets ces critiques de manière encore plus virulente vis à vis... des mutateurs...
    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. #205
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par koala01 Voir le message
    J'ai, encore une fois, l'impression que vous avez des approches, si pas fragmentaires, en tout cas restrictives ou erronées de ce que peuvent représenter l'encapsulation, la loi demeter, ou le concept orienté objet lui-même.
    Je ne suis pas un ferme partisan de la loi de Demeter, je dois dire, pour des raisons d'efficacité principalement. Dans mon domaine (embarqué temps réel), c'est un élément crucial et non-négociable.

    Toutefois, je ne vois vraiment pas en quoi une propriété viole cette règle, dans le sens qu'elle ne permet ni plus, ni moins de choses qu'un accesseur... Après, si effectivement tu publies par propriété/accesseur un attribut d'une sous-classe, tu t'exposes à un paquet d'emm.... gros comme ça, mais là encore, le concept de propriété n'est en rien responsable de ça.

    Au contraire : pour respecter Demeter, tu vas souvent devoir écrire un (trop) grand nombre de wrappers, chose lourde et franchement pesante. Les propriétés permettent de faire ceci de façon nettement plus élégante qu'une tripaille d'accesseurs/mutateurs/foncteurs, et n'interdisent absolument pas la création de méthodes de "service macroscopique" avec propagation dudit service aux sous-classes...

    Par rapport aux exemple précités : c'est pourtant un cas tout à fait légitime d'additionner deux attributs différents de deux objets différents... D'une part, deux objets peuvent parfaitement décrire un système, mais de deux points de vue différents, et ne pas se connaître ni de près, ni de loin. D'autre part, l'attribut de l'un peut refléter l'attribut de l'autre. De plus, tu es parti du principe que cela représentait deux instance de la même classe : et pourquoi donc devrait-ce être le cas ?

    Reformulé, on pourrait écrire : StatisticObject.TotalNumberOfCommands += aTempCommandObject.Size ; : cela a un sens, et permet d'éviter de créer une fonction spécifique d'ajout (imposant de connaître les types de classes impliqués) dans une fonction qui n'a de sens que pour le programme appelant, ou de créer une classe spécifique pour centraliser ces deux sous-classes... Tu ne peux pas créer des prototypes pour toutes les références croisées possibles et imaginables, c'est une évidence. Tu ne peux pas non plus prévoir toutes les utilisations possibles et imaginables de ta classe si elle est un tant soit peu générique / bas niveau. Tu vas donc reporter ce problème vers l'appelant en l'obligeant à définir un paquet de classes d'adaptation.

    Tu vas me dire "Quel intérêt de faire ça ?", peut-être : ben c'est d'un intérêt évident quand tu écris beaucoup de code-passerelle, c'est à dire du code de transfert entre deux mondes. Or, beaucoup de mes librairies sont justement utilisées par un code de ce type. Actuellement, en C++, je n'ai pas d'autre choix que de fournir des accesseurs, ou directement l'attribut dans certains cas... Performances et simplicité d'utilisation obligent.

    Le déport dans le code appelant au travers des propriétés (ou des accesseurs) est, justement, une partie de la solution à ce problème. Certes, dans du code 100% métier, ce cas de figure apparaît rarement. Quand tu fais des librairies génériques dans 99% des cas, comme moi, tu apprends vite à savoir déléguer à l'appelant et/ou à imposer des contrats. De plus, je dois avouer que la "pureté" du code source et le respect de Grands Principes (les majuscules sont volontaires) sont le cadet de mes soucis : mon but premier, à la création d'une classe, est d'avoir une API claire, concise et pratique à utiliser. Le reste, c'est ma tripaille interne et ma sauce, je n'ai pas à exposer à l'utilisateur mes contraintes de code et/ou mes évolutions d'interface.
    Or, justement, les propriétés me permettent de pas mal blinder à ce niveau sans rendre le code appelant infect... J'ai tendance à pleurer à chaque fois que je vois un code généré C++ de lecture XML, où l'on finit par penser que le concepteur du générateur a été payé à la parenthèse produite ! Les propriétés simplifieraient grandement ce type de code, sans modifier en quoi que ce soit les fonctionnalités ou les restrictions d'accès.

    Question de point de vue, donc...

    Citation Envoyé par koala01 Voir le message
    J'admets que, dans certains cas, un accesseur puisse s'avérer intéressant, mais, très souvent, il subira l'énorme inconvénient de fournir un acces "global" (dans le sens de disponible dans n'importe quelle portion de code) à quelque chose dont l'acces devrait être particulièrement restreint.
    Une propriété fait ce que tu veux qu'elle fasse. Tu peux l'utiliser pour n'importe quelle donnée, et définir le niveau d'accès que tu souhaites avoir (R, W, RW).
    J'ai également beaucoup de mal à voir en quoi restreindre l'accès à une donnée comme la cardinalité d'un container, ou l'accès aux éléments unitaires, pose le moindre problème.

    Typiquement, une propriété peut remplacer les opérateurs () et [] de façon tout à fait confortable, en n'autorisant que la lecture et non pas l'écriture.

    Ce que je vois surtout, c'est que le programme appelant n'est pas à modifier lorsque l'on transforme un attribut "simple" (= une variable de classe uniquement, donc) en un attribut "complexe" (= sa modification déclenche un trigger interne). Typiquement, tu peux transformer une classe relativement figée et statique en quelque chose de dynamique réagissant "en direct-live" à ses modifications.

    Après, je te concède que le niveau de visibilité published peut avoir son importance dans une implémentation correcte des propriétés, tout comme des concepts tels que le inherited ou le deprecated...
    N.B. : les liens de ce paragraphe renvoient vers des cours/posts Delphi.

    C'est quand même un cas concret dans le développement en situation réelle : on implémente une première version d'un composant, qui va ensuite évoluer en quelque chose de plus complexe. Au moins, le code source appelant n'a pas à être modifié, et cela a évité de créer initialement des tonnes d'accesseurs plus ou moins inutiles (et coûteux en temps de développement / tests !!).

    D'un point de vue strictement pratique, j'ai rarement vu quelqu'un dire que la VCL Borland soit mal foutue côté architecture... Elle fait pourtant un usage plus qu'intensif et constant des propriétés, et c'est aussi pour ça qu'elle est simple et pratique d'utilisation...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  6. #206
    screetch
    Invité(e)
    Par défaut
    la VCL est mal foutue (il fallait bien quelqu'un pour le dire...)
    les objets melangent plusieurs cmportements (beaucoup, en fait) parce qu'apparemment quand on fait de l'IHM on ne connait rien a la composition, on ne connait que l'héritage (c'est un trait très commun qui se retrouve partout de toutes facons)
    => classes bloatées qui ont 43 rôles différents.

    un exemple simple : si je veux une image qui se comporte comme un bouton, comment je fais ?
    dériver de bouton et redéfinir la fonction d'image?
    dériver d'image et redéfinir la fonction de clic ?
    dériver des deux a la fois ?

    si le comportement (ce qui se passe lorsque je clique...) n'avait pas été integré au rendu (comment déssiner le composant) alors ca serait très simple; il y aurait un comportement "bouton" et un widget "image", il suffit de coller ces deux la ensemble et on a une image cliquable.



    MacLak tu parles de la lisibilité, je trouve que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    StatisticObject.TotalNumberOfCommands += aTempCommandObject.Size ;
    est particulièrement crade si l'un des deux a un effet de bord quelconque, et très illisible. si ce ne sont que des attributs publics (ce qui ne change rien a l'affaire)on est sur qu'il n'y a pas d'effet de bord. Si cette ligne se transforme en appel de fonctions, moi je râle; je ne veux pas aller voir ce que la propriété machin fait réellement.
    lorsqu'il y a une propriété il peut y avoir mensonge de l'interface; par exemple, une propriété "size" qui calcule la taille en O(n) comme pour une liste chaînée, si c'est une méthode j'irai faire attention. si c'est un attribut, c'est simplement stocké.
    et si c'est une propriété, ca ressemble a un attribut mais en fait ca mobilise le processeur.

  7. #207
    Expert éminent
    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 : 40
    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
    Points : 7 752
    Points
    7 752
    Par défaut
    Citation Envoyé par screetch Voir le message
    l
    MacLak tu parles de la lisibilité, je trouve que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    StatisticObject.TotalNumberOfCommands += aTempCommandObject.Size ;
    est particulièrement crade si l'un des deux a un effet de bord quelconque, et très illisible. si ce ne sont que des attributs publics (ce qui ne change rien a l'affaire)on est sur qu'il n'y a pas d'effet de bord. Si cette ligne se transforme en appel de fonctions, moi je râle; je ne veux pas aller voir ce que la propriété machin fait réellement.
    lorsqu'il y a une propriété il peut y avoir mensonge de l'interface; par exemple, une propriété "size" qui calcule la taille en O(n) comme pour une liste chaînée, si c'est une méthode j'irai faire attention. si c'est un attribut, c'est simplement stocké.
    et si c'est une propriété, ca ressemble a un attribut mais en fait ca mobilise le processeur.
    Mais si c'est un attribut, tu veux aussi interdire la modification directe depuis l'extérieur de ta classe Liste, donc on est d'accord que ça passe obligatoirement par une méthode dans les deux cas.

    Si tu fais un size() ou un count(), tu n'as pas réellement d'indication non plus sur ce qui est fait sous le rideau. Cela pourrait être un simple "return this->size".

    En fait ce qui te dérange c'est plutôt la confusion possible entre une variable membre et la propriété du point de vue de la syntaxe d'écriture? Je pense que c'est une question d'habitude et de documentation.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Au contraire : pour respecter Demeter, tu vas souvent devoir écrire un (trop) grand nombre de wrappers, chose lourde et franchement pesante. Les propriétés permettent de faire ceci de façon nettement plus élégante qu'une tripaille d'accesseurs/mutateurs/foncteurs, et n'interdisent absolument pas la création de méthodes de "service macroscopique" avec propagation dudit service aux sous-classes...
    Pas forcément...

    C'est là que peut entrer en jeu l'amitié qui, si elle est correctement utilisée, renforcera encore l'encapsulation

    Par rapport aux exemple précités : c'est pourtant un cas tout à fait légitime d'additionner deux attributs différents de deux objets différents... D'une part, deux objets peuvent parfaitement décrire un système, mais de deux points de vue différents, et ne pas se connaître ni de près, ni de loin. D'autre part, l'attribut de l'un peut refléter l'attribut de l'autre. De plus, tu es parti du principe que cela représentait deux instance de la même classe : et pourquoi donc devrait-ce être le cas ?

    Reformulé, on pourrait écrire : StatisticObject.TotalNumberOfCommands += aTempCommandObject.Size ; : cela a un sens, et permet d'éviter de créer une fonction spécifique d'ajout (imposant de connaître les types de classes impliqués) dans une fonction qui n'a de sens que pour le programme appelant, ou de créer une classe spécifique pour centraliser ces deux sous-classes...
    Rien ne t'empêche, en t'aidant de l'amitié, de créer une classe dont la responsabilité sera d'assurer la conversion de ces deux types qui ne "se connaissent absolument pas"
    Tu ne peux pas créer des prototypes pour toutes les références croisées possibles et imaginables, c'est une évidence. Tu ne peux pas non plus prévoir toutes les utilisations possibles et imaginables de ta classe si elle est un tant soit peu générique / bas niveau. Tu vas donc reporter ce problème vers l'appelant en l'obligeant à définir un paquet de classes d'adaptation.
    Avec les template, tu peux en arriver à n'écrire qu'une fois les choses
    Tu vas me dire "Quel intérêt de faire ça ?", peut-être : ben c'est d'un intérêt évident quand tu écris beaucoup de code-passerelle, c'est à dire du code de transfert entre deux mondes. Or, beaucoup de mes librairies sont justement utilisées par un code de ce type. Actuellement, en C++, je n'ai pas d'autre choix que de fournir des accesseurs, ou directement l'attribut dans certains cas... Performances et simplicité d'utilisation obligent.
    tu devrais peut être relire cette entrée de la FAQ en ce qui concerne l'amité
    Quand tu fais des librairies génériques dans 99% des cas, comme moi, tu apprends vite à savoir déléguer à l'appelant et/ou à imposer des contrats. De plus, je dois avouer que la "pureté" du code source et le respect de Grands Principes (les majuscules sont volontaires) sont le cadet de mes soucis : mon but premier, à la création d'une classe, est d'avoir une API claire, concise et pratique à utiliser. Le reste, c'est ma tripaille interne et ma sauce, je n'ai pas à exposer à l'utilisateur mes contraintes de code et/ou mes évolutions d'interface.
    Les deux ne sont absolument pas antinomiques, sans passer par les propriétés
    Une propriété fait ce que tu veux qu'elle fasse. Tu peux l'utiliser pour n'importe quelle donnée, et définir le niveau d'accès que tu souhaites avoir (R, W, RW).
    J'ai bien compris qu'un propriété peut être, au choix, un accesseur, un mutateur ou le couple des eux...

    Il n'empêche que cela renvoi un "détail d'implémentation" qui n'a, normalement, souvent pas lieu d'être exposé...
    J'ai également beaucoup de mal à voir en quoi restreindre l'accès à une donnée comme la cardinalité d'un container, ou l'accès aux éléments unitaires, pose le moindre problème.
    La cardinalité d'un containeur, si la classe agit comme tel, cela ne me dérange pas si on l'expose, mais je ne vois pas en quoi écrire monContainer.size est plus facile que d'écrire monContainer.size()...

    D'autant plus que nous avons, comme le fait remarquer screetch, qu'au moins, nous sommes en mesure, dans le deuxième cas, de nous rendre plus facilement compte qu'il s'agit, effectivement, d'une fonction
    Typiquement, une propriété peut remplacer les opérateurs () et [] de façon tout à fait confortable, en n'autorisant que la lecture et non pas l'écriture.
    [/QUOTE]Le problème de lecture et / ou de l'écriture n'en est pas un, étant donné que nous convenons tous les deux que rien n'empêche de prévoir les restrictions utiles, à la manière de ce que le sucre syntaxique remplace.

    Le problème est, surtout pour l'opérateur [], que tu expose un détail d'implémentation qu'il serait bien plus intéressant d'exposer sous la forme d'un itérateur (parce que, si tu dois renvoyer l'objet contenu dans une liste, l'opérateur [] n'a strictement plus lieur d'être...)

    Ce que je vois surtout, c'est que le programme appelant n'est pas à modifier lorsque l'on transforme un attribut "simple" (= une variable de classe uniquement, donc) en un attribut "complexe" (= sa modification déclenche un trigger interne). Typiquement, tu peux transformer une classe relativement figée et statique en quelque chose de dynamique réagissant "en direct-live" à ses modifications.
    C'est quand même un cas concret dans le développement en situation réelle : on implémente une première version d'un composant, qui va ensuite évoluer en quelque chose de plus complexe. Au moins, le code source appelant n'a pas à être modifié, et cela a évité de créer initialement des tonnes d'accesseurs plus ou moins inutiles (et coûteux en temps de développement / tests !!).
    On en revient toujours au même problème, qui est que les mutateurs et ou les accesseurs sont, par nature, à éviter...

    Une fois que tu as compris cela ( c'est ce que je répète depuis de nombreux posts), on comprend pourquoi les propriétés sont, elles aussi, à éviter.

    Si, pour une raison ou une autre, une modification doit être apportée, il est bien plus intéressant d'invoquer une fonction dont le nom correspond au comportement attendu et de lui fournir les arguments nécessaires, sans que cette fonction ne s'appelle setMachin, d'autant plus que l'on n'a normalement pas à s'inquiéter, en invoquant cette fonction, de la valeur de l'attribut avant la modification: C'est à la fonction de vérifier s'il faut apporter ou non la modification demandée.

    Evidemment, lorsqu'il s'agit d'un container, le fait de savoir de combien d'objet il est composé fait partie des services que l'on attend de sa part...

    Mais je ne vois pas en quoi cela simplifie les chose d'avoir une propriété size au lieu de la fonction membre constante size.
    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

  9. #209
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par screetch Voir le message
    la VCL est mal foutue (il fallait bien quelqu'un pour le dire...)
    Tellement mal foutue que Lazarus cherche absolument à la reproduire à 100%, au lieu d'utiliser quelque chose d'autre...

    Citation Envoyé par screetch Voir le message
    un exemple simple : si je veux une image qui se comporte comme un bouton, comment je fais ?
    dériver de bouton et redéfinir la fonction d'image?
    dériver d'image et redéfinir la fonction de clic ?
    dériver des deux a la fois ?
    Tu vas avoir du mal de dériver des deux à la fois... Delphi ne supporte pas l'héritage multiple.

    Quant au faux problème du comportement, c'est très simple pourtant : tu définis un bouton (comportement le plus complexe, donc celui que l'on ne "refait" pas), et tu écris sa méthode Paint... Éventuellement en appelant le Paint d'une image non stockée sur la fiche, d'ailleurs. Tu as très exactement le même problème avec les MFC ou l'API Win32, et c'est d'ailleurs tout à fait normal : tout est canvas et window, les variantes de comportement ne sont justement que des variantes.

    Autre solution, nettement plus élégante : tu prends un TImageButton et tu règles ses propriétés correctement...

    Citation Envoyé par screetch Voir le message
    si le comportement (ce qui se passe lorsque je clique...) n'avait pas été integré au rendu (comment déssiner le composant) alors ca serait très simple; il y aurait un comportement "bouton" et un widget "image", il suffit de coller ces deux la ensemble et on a une image cliquable.
    VCL = Visual Component Library, pour mémoire et information. C'est une bibliothèque RAD, et non pas une base élémentaire comme pourraient l'être les MFC (où le "F" signifie Foundation, je te rappelle, et non pas F**king Very High Level Interface).

    En VCL, tu rends une image cliquable très simplement, la différence étant que, justement, tu n'as PAS besoin de séparer le comportement du rendu dans 99.99% des cas... Les composants sont donc adaptés au cas nominal, interdire ou augmenter ce comportement nominal prendra du temps. Réciproquement, en MFC, c'est plutôt "tout ce qui n'est pas codé est inexistant" : ça fait des exécutables plus compacts, certes, mais dans 90% des cas tu passes ton temps à redéfinir 20 fois les mêmes choses.

    Mais vouloir un comportement "roots" avec des outils RAD (ou le contraire, d'ailleurs), tu n'as pas l'impression de t'être planté de librairie ?

    Citation Envoyé par screetch Voir le message
    Si cette ligne se transforme en appel de fonctions, moi je râle; je ne veux pas aller voir ce que la propriété machin fait réellement.
    Arrêtes un peu la mauvaise foi : c'est exactement pareil quand tu appelles une méthode qui te renvoie soit un attribut directement, soit qui effectue un comportement complexe. Tu n'as PAS à savoir ce que ça fait, et tu as des contrats si l'état initial / final ont une importance quelconque (= des effets de bord). Tu as aussi une documentation de la classe, et avec des avertissements sur les coûts CPU anormaux si elle n'a pas été écrite par un incapable fini.

    Les problèmes que tu décris sont plus liés à une documentation minable et une conception faite pendant un abrutissement devant la Star'Ac qu'à l'utilisation de propriétés à la place d'accesseurs (ou vice-versa, d'ailleurs).

    Citation Envoyé par screetch Voir le message
    lorsqu'il y a une propriété il peut y avoir mensonge de l'interface; par exemple, une propriété "size" qui calcule la taille en O(n) comme pour une liste chaînée, si c'est une méthode j'irai faire attention. si c'est un attribut, c'est simplement stocké.
    et si c'est une propriété, ca ressemble a un attribut mais en fait ca mobilise le processeur.
    C'est exactement la même chose pour les méthodes : ce que tu décris, c'est simplement du code de goret, peu importe la manière dont il est appelé. Obtenir la taille d'une liste chaînée en la parcourant à chaque fois, faut ne pas être fini et/ou n'avoir absolument aucune conscience des performances...

    Une bonne propriété size renvoie un attribut interne à la lecture, redimensionne le container à l'écriture, et l'attribut est mis à jour à chaque modification du container. Bref, un size() + resize() en un seul point d'accès logique, au lieu de devoir retenir deux fonctions, et le tout prévu pour un minimum syndical de performances.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  10. #210
    screetch
    Invité(e)
    Par défaut
    le RAD est hors sujet ici
    les propriétés sont un détail, avoir des parenthèses ou pas ca n'a aucune incidence

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class AvecAccesseur
    {
      int m_value;
    public:
      int& value() { return m_value; }
      const int& value() const { return m_value; }
    };
    permet d'ecrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    avecAccesseur.value() += avecAutreAccesseur.value();
    pas de get, pas de set. c'est vraiment un détail que ca soit possible de retirer les parenthèses en D et pas en C++, franchement.

    ce qui est interessant avec les propriétés c'est surtout la sérialisation et 'introspection, le reste est plutôt anecdotique. c'est comme dire que "struct" est un meilleur mot-lé que "class" ou l'inverse, ca n'apporte rien au débat.

  11. #211
    screetch
    Invité(e)
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Une bonne propriété size renvoie un attribut interne à la lecture, redimensionne le container à l'écriture, et l'attribut est mis à jour à chaque modification du container. Bref, un size() + resize() en un seul point d'accès logique, au lieu de devoir retenir deux fonctions, et le tout prévu pour un minimum syndical de performances.
    ca ne donnera des hémorroïdes a personne d'ecrire resize() ou size()
    ce qui a aussi l'avantage de documenter.

  12. #212
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Quant au faux problème du comportement, c'est très simple pourtant : tu définis un bouton (comportement le plus complexe, donc celui que l'on ne "refait" pas), et tu écris sa méthode Paint... Éventuellement en appelant le Paint d'une image non stockée sur la fiche, d'ailleurs. Tu as très exactement le même problème avec les MFC ou l'API Win32, et c'est d'ailleurs tout à fait normal : tout est canvas et window, les variantes de comportement ne sont justement que des variantes.
    Je te conseille de regarder un peu WPF. C'est usine çà gaz par moment, mais ils ont une manière de gérer ce genre de situation assez différente, qui me semble bien plus intéressante. En gros, un bouton peut contenir d'autres contrôles, par exemple, une image, u texte, une listbox... D'ailleurs, un élément d'une listbox peut elle aussi contenir d'autres contrôles...
    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.

  13. #213
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par koala01 Voir le message
    C'est là que peut entrer en jeu l'amitié qui, si elle est correctement utilisée, renforcera encore l'encapsulation
    L'amitié impose de pouvoir modifier la classe ciblée, ce qui n'est pas toujours ni possible, ni souhaitable.

    Citation Envoyé par koala01 Voir le message
    Rien ne t'empêche, en t'aidant de l'amitié, de créer une classe dont la responsabilité sera d'assurer la conversion de ces deux types qui ne "se connaissent absolument pas"
    Ce qui impose l'écriture d'une classe de plus... Ce que justement, je cherche à éviter comme la peste. Une classe wrapper, c'est des lignes de code en plus, donc une source de bug potentielle. Autant limiter ça au code appelant : si tu veux généraliser le principe (et rendre ta classe réutilisable), ta classe-wrapper devra ne pas introduire de comportements spécifiques au code métier appelant, donc ne réellement définir QUE du wrapping... Ce qui fait que tu vas, au final, écrire du code proche (sur le fond) de celui des propriétés bien que différent dans la forme.
    Si, par contre, elle introduit du code métier, alors elle n'est plus générique, devient spécifique au code métier appelant, et n'a alors plus aucune raison d'être car elle introduit une couche supplémentaire inutile car non réutilisable.

    Citation Envoyé par koala01 Voir le message
    Avec les template, tu peux en arriver à n'écrire qu'une fois les choses
    Encore faut-il que ce soit possible, et c'est rarement le cas. Pour te donner un exemple, tu vas avoir du mal à définir un comportement commun (et donc une interface "unique") entre un bus CAN et des lignes analogiques... Pourtant, il peut être utile de connaître le nombre total de données acquises/transmises, peu importe le médium. Un template va t'imposer, à certains moments, l'écriture d'une interface "commune" qui pourrait très bien ne pas avoir réellement de sens pour l'une des classes, et n'en avoir que pour l'appelant. Tu vas donc complexifier une classe avec des informations "inutiles" pour respecter une interface commune : OK, mais franchement, je trouve que c'est du boulot inutile et pas forcément réutilisable (donc pas nécessaire de se taper une interface from scratch pour ça).

    Citation Envoyé par koala01 Voir le message
    Il n'empêche que cela renvoi un "détail d'implémentation" qui n'a, normalement, souvent pas lieu d'être exposé...
    Pas plus qu'un accesseur et/ou un attribut public, y compris "public" via un friend.

    Citation Envoyé par koala01 Voir le message
    La cardinalité d'un containeur, si la classe agit comme tel, cela ne me dérange pas si on l'expose, mais je ne vois pas en quoi écrire monContainer.size est plus facile que d'écrire monContainer.size()...
    Le fait que size() n'est pas affectable, contrairement à la propriété homonyme, donc tu devras écrire une méthode resize() supplémentaire.
    Pour augmenter ta capacité de 10 éléments, tu devras donc écrire monContainer.resize(monContainer.size()+10) au lieu de monContainer.size+=10... Ou développer une troisième méthode "relative_resize()" qui prends un delta.
    Cela commence à faire beaucoup de méthodes et des lignes d'appel bien longues, je trouve !!

    Citation Envoyé par koala01 Voir le message
    D'autant plus que nous avons, comme le fait remarquer screetch, qu'au moins, nous sommes en mesure, dans le deuxième cas, de nous rendre plus facilement compte qu'il s'agit, effectivement, d'une fonction
    Et j'y réponds la même chose : utilises des classes mieux documentées et/ou ne les fais pas faire par des incapables.

    Citation Envoyé par koala01 Voir le message
    Le problème est, surtout pour l'opérateur [], que tu expose un détail d'implémentation qu'il serait bien plus intéressant d'exposer sous la forme d'un itérateur (parce que, si tu dois renvoyer l'objet contenu dans une liste, l'opérateur [] n'a strictement plus lieur d'être...)
    En quoi est-ce un détail d'implémentation ??? Que tu appelles l'obtention d'une cardinalité "property size", "getSize()", "count()" ou "size()", c'est la même chose derrière. Simplement, certaines formes d'écriture sont plus pratiques pour l'appelant. Toutes ces formes exposent autant (ou aussi peu, je dirais) les détails d'implémentation réels derrière.

    Citation Envoyé par koala01 Voir le message
    On en revient toujours au même problème, qui est que les mutateurs et ou les accesseurs sont, par nature, à éviter...
    Ce qui va rendre l'écriture de containers un minimum performants assez difficile...
    Que tu appelles ta méthode de lecture getElementAt(i) ou [ i ], quelle est la différence réelle ? Pour moi, les deux sont des accesseurs... Le premier est simplement plus lourd et pénible à utiliser que le second.
    De plus, comme les deux formes sont (sémantiquement parlant) équivalentes, il n'y a pas plus de problèmes à définir un itérateur avec une propriété qu'avec des méthodes.

    Citation Envoyé par koala01 Voir le message
    Si, pour une raison ou une autre, une modification doit être apportée, il est bien plus intéressant d'invoquer une fonction dont le nom correspond au comportement attendu et de lui fournir les arguments nécessaires, sans que cette fonction ne s'appelle setMachin, d'autant plus que l'on n'a normalement pas à s'inquiéter, en invoquant cette fonction, de la valeur de l'attribut avant la modification: C'est à la fonction de vérifier s'il faut apporter ou non la modification demandée.
    Ce qui revient pour moi à la même chose : tu n'as finalement fait que renommer ton accesseur/mutateur mais il remplit exactement le même rôle. Un accesseur n'est pas caractérisé par un "get" ou "set" en préfixe, j'espère que l'on est d'accord sur ce point...

    Citation Envoyé par koala01 Voir le message
    Mais je ne vois pas en quoi cela simplifie les chose d'avoir une propriété size au lieu de la fonction membre constante size.
    Cf. exemple d'utilisation ci-dessus, avec le resize.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  14. #214
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par screetch Voir le message
    les propriétés sont un détail, avoir des parenthèses ou pas ca n'a aucune incidence
    Si c'est sans importance, pourquoi tu combats autant le fait de les enlever ? Faudrait savoir, ou être cohérent...
    Désolé, mais là, c'était trop tentant.

    Citation Envoyé par screetch Voir le message
    ca ne donnera des hémorroïdes a personne d'ecrire resize() ou size()
    Parce que tu as pris l'habitude de te taper ça. Moi, j'ai pris l'habitude d'avoir plus simple et, surtout, d'avoir quelque chose de nettement plus proche du raisonnement humain tout en restant à un niveau d'efficacité proche de l'optimal. Via des propriétés, la taille (pour continuer cet exemple) est alors une entité à part entière, UNIQUE, que tu manipules en fonction de tes besoins et non pas quelque chose de "diffus" accédé au travers de 3 ou 4 fonctions plus ou moins cohérentes.
    Via des méthodes classiques, la seule façon d'arriver à ce niveau de centralisation serait de définir une sous-classe "Size", avec quelques primitives, qui permettrait d'avoir un point central d'accès aux fonctions liées à la taille du container.
    Ce qui alourdit très nettement l'écriture et l'utilisation dudit container, sans parler des risques de pertes de performances... Inconvénients que n'ont pas les propriétés. La "tripaille" interne reste tout aussi "diffuse" avec les propriétés (plusieurs méthodes privées nécessaires pour réaliser les actions atomiques), donc pas de changements majeurs pour le développeur, mais l'interface publique (celle de l'utilisateur) est naturellement centralisée.

    Citation Envoyé par screetch Voir le message
    ce qui a aussi l'avantage de documenter.
    Parce que tu appelles ça documenter ???? Pour toi, documenter, c'est simplement donner un nom à peu près correct à une méthode, et point barre ???
    Ben ils sont vachement tolérants, dans ta boîte... Si un de mes dévs me fait ce coup-là sur une doc client, je le crucifie.


    Citation Envoyé par JolyLoic Voir le message
    Je te conseille de regarder un peu WPF. C'est usine çà gaz par moment <snip>
    Ce qui pour moi est rédhibitoire pour faire des IHMs. C'est certes bien mieux que les MFC, mais c'est loin d'être parfait ou du niveau d'un vrai RAD. Et il se pose le problème (plus ou moins épineux) de la connexion avec des DLL natives : autant certains cas de figure sont très simples, autant d'autres sont franchement galères vu l'impossibilité de franchir (via debug) la barrière entre le natif et le managé.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    <snip>
    Pas plus qu'un accesseur et/ou un attribut public, y compris "public" via un friend.
    Si ce n'esxt que l'amitié permet de n'exposer quelque chose qu'à ce qui en a réellement besoin.

    Par nature, toute fonction publique (qu'il s'agisse d'un accesseur, d'un mutateur, d'une fonction normale) est susceptible d'être appelée de "n'importe où dans le code", dés le moment où le contenu de l'objet est connu.

    Or, je suis désolé, mais il y a quantité de chose qui doivent n'être connues que d'un minimum d'autres choses extérieures.

    De ce point de vue, l'amitié est, définitivement, la meilleure manière de travailler, si l'on excepte l'héritage (qui n'est pas toujours cohérent)

    Le principe des propriétés, encore une fois, c'est que cela facilite le fait d'exposer des choses qui... auraient en tout casdu être exposées de manière beaucoup plus restrictive.
    Le fait que size() n'est pas affectable, contrairement à la propriété homonyme, donc tu devras écrire une méthode resize() supplémentaire.
    Au contraire, si une même fonction a -- en dehors du polymorphisme, qui garde quand même la sémantique de la fonction -- deux comportement clairement opposés, c'est plutôt le signe d'une mauvaise compréhension de ce que l'on attend de cette fonction..
    Pour augmenter ta capacité de 10 éléments, tu devras donc écrire
    monContainer.resize(monContainer.size()+10) au lieu de monContainer.size+=10... Ou développer une troisième méthode "relative_resize()" qui prends un delta.
    Tu sembles oublier que, lorsque tu crées un conteneur, il ne suffit pas de modifier la valeur d'un attribut pour, effectivement, redimensionner ce container...

    De plus, pour redimensionner un container, tu as classiquement trois souhaits possibles:
    1. le redimensionner à une valeur déterminée, indépendante de la taille d'origine
    2. Réduire sa taille de un (ou de plusieurs) élément(s)
    3. augmenter sa taille de un (ou de plusieurs) élément(s)
    Dans le premier cas, il s'agit de redimensionner le conteneur sur base de la taille d'un autre conteneur (pas forcément compatible), d'un compteur ou d'une information récupérée par ailleurs.

    Dans le deuxième cas, on peut estimer que cette diminution de la taille correspond... au retrait d'élément du conteneur

    Et, dans le troisième cas, on peut, à l'inverse, estimer que cette augmentation de taille correspond... à l'ajout d'élément dans le contenur.

    Nous avons donc bel et bien affaire à... trois comportements différents qui...méritent d'être clairement différenciés.

    Maintenant, si la fonction d'ajout ou de retrait d'élément utilise resize, ma fois, pourquoi pas

    Après tout, un simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void MaClass::add(/*...*/)
    {
        resize(m_size+1);
        /* suite de la logique, dépedant du conteneur mis en oeuvre
    }
    le fait très bien, quand ce n'est pas, encore mieux un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MaClass::add(/*...*/)
    {
        m_items.add(/*...*/)
    }
    Cela commence à faire beaucoup de méthodes et des lignes d'appel bien longues, je trouve !!
    Beaucoup de méthodes... faut pas exagérer...

    Dans le cadre de la gestion de la taille, tu as, au total quatre fonctions à créer, qui, au moins, définissent clairement ce qu'elles font:
    • size() : renvoi du nombre d'élément
    • resize(int) : redéfini le nombre d'éléments en fonction du nombre donné en paramètre
    • add( type) : ajoute un objet
    • remove(iterateur) : retire un objet

    Bon, allez, je t'accorde que, pour add et remove, on peut aussi envisager une version prenant un itérateur de début et un itérateur de fin
    et, en terme de ligne de code, ca ne fait pas grand chose non plus.

    Par contre, cela permet d'avoir des comportement clairement définis, qui ne risquent pas de prêter à confusion

    Et j'y réponds la même chose : utilises des classes mieux documentées et/ou ne les fais pas faire par des incapables.

    En quoi est-ce un détail d'implémentation ??? Que tu appelles l'obtention d'une cardinalité "property size", "getSize()", "count()" ou "size()", c'est la même chose derrière. Simplement, certaines formes d'écriture sont plus pratiques pour l'appelant. Toutes ces formes exposent autant (ou aussi peu, je dirais) les détails d'implémentation réels derrière.

    Ce qui va rendre l'écriture de containers un minimum performants assez difficile...
    Que tu appelles ta méthode de lecture getElementAt(i) ou [ i ], quelle est la différence réelle ?
    La sémantique que l'on accorde généralement à l'opérateur ()...

    Cela n'a l'air de rien, mais, modifier la sémantique d'un opérateur ou d'une fonction est la pire des choses qui soient.

    Ainsi, l'opérateur [] véhicule une sémantique d'acces direct, aléatoire.

    C'est très bien lorsque la collection, effectivement, basée sur un tableau, mais cela perd tout son sens lorsque la collection est basée sur... une liste ou n'importe quoi d'autre...

    C'est la raison pour laquelle, plutôt que de renvoyer une référence (constante ou non)sur l'élément de la collection, je suis clairement partisan de... définir un itérateur imbriqué et d'utiliser de préférence celui-ci comme valeur de retour, si tant est, toujours, qu'il soit opportun de permettre "à l'extérieur" d'accéder au contenu de la collection.

    De cette manière, tu arrives même à éviter, au minimum dans une certaine mesure, la réécriture des fonctions qui utilisent l'itérateur si, un jour, tu décide de modifier la manière dont la collection est effectivement gérée en interne.
    Pour moi, les deux sont des accesseurs... Le premier est simplement plus lourd et pénible à utiliser que le second.
    Comme je l'ai dit plus haut, tu fait l'impasse sur la sémantique, et c'est bien dommage
    Ce qui revient pour moi à la même chose : tu n'as finalement fait que renommer ton accesseur/mutateur mais il remplit exactement le même rôle. Un accesseur n'est pas caractérisé par un "get" ou "set" en préfixe, j'espère que l'on est d'accord sur ce point...
    Le fait est que la sémantique que tu vas donner au nom de ta fonction importe tout autant que le comportement qu'elle met en oeuvre.

    Nous sommes, effectivement, d'accord sur le fait qu'un accesseur n'est pas caractérisé par la présence du préfixe "get" ou "set", mais, là où nous ne sommes visiblement pas d'accord c'est:
    1. sur le fait qu'une fonction doit avoir un comportement déterminé, et non un comportement susceptible de varier selon les circonstances
    2. sur le fait que le meilleur moyen pour quelqu'un qui lit le code le comprenne facilement, c'est que le nom de chaque fonction représente clairement le comportement qu'elle met en oeuvre
    3. sur le fait qu'il faut autant que faire se peut éviter de changer la sémantique généralement accordée à des fonctions ou des opérateurs couremment utilisés par ailleurs

    Et, au passage, de manière générale, un accesseur non constant est, pour moi, une aberration... Surtout s'il renvoie une référence sur l'attribut (cf effective C++, entre autres)
    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

  16. #216
    Invité
    Invité(e)
    Par défaut
    Ma priorité n'est pas que je puisse avoir le meilleur design mais que ce design puisse changer, et si possible rapidement. Je ne me pose pas la question si je dois faire un accesseur, un mutateur, etc... tant que c'est documenté.

    Les propriétés, quand elles sont bien faites, permettent de rajouter comme un espèce de wrapper autour des fonctionnalités offertes par une classe. Si il y a une modif à faire, hop, on change un champ en méthode mais le contrat et le code de l'appelant reste le même.

    Ce qui était vraiment bien en Delphi, c'est que ce wrapper ne coûtait rien en performance vu qu'on pouvait mapper une propriété sur un champ. Donc on pouvait les utiliser tout le temps sans que ca fasse plein d'appels de méthode en plus.

    En fait je pense qu'il faut avoir codé sous Delphi pour comprendre cet intérêt (que MacLak a bien expliqué) et combien ca accélère les choses.

    Renvoyer une reférence sur un objet en C++, ce n'est pas pareil, ca ne permet pas de transformer un champ en méthode de manière transparente.

  17. #217
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 537
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 537
    Points : 2 548
    Points
    2 548
    Par défaut
    Je ne vois pas pourquoi enlever ces parenthèses est si grave que cela. C'est au contraire un moyen d'abstraction intéressant je trouves.

    Pour le reste, les variadic templates et le fait de pouvoir faire des templates sur du int, float ou même char[], c'est quand même le bonheur. Ça n'a pas été cités parmis les top features manquantes au C++. Pourtant, c'est parmis celles dont je me sers le plus.

  18. #218
    screetch
    Invité(e)
    Par défaut
    certes, si on pouvait passer a autre chose que les property maintenant...

  19. #219
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Si ce n'esxt que l'amitié permet de n'exposer quelque chose qu'à ce qui en a réellement besoin.
    Ou de complexifier inutilement quelque chose qui ne doit pas l'être... Chaque médaille a son revers, tu le sais aussi bien que moi.

    Citation Envoyé par koala01 Voir le message
    Par nature, toute fonction publique (qu'il s'agisse d'un accesseur, d'un mutateur, d'une fonction normale) est susceptible d'être appelée de "n'importe où dans le code", dés le moment où le contenu de l'objet est connu.
    Et alors ? Si c'est en lecture seule, ça n'a aucune importance, sauf celui de te permettre d'avoir du code d'introspection.
    Si c'est en écriture, tu as des tonnes de mécanismes permettant de blinder cet aspect (sections critiques, enregistrement via par exemple le TLS, encapsulation et/ou private implementation, singletons, pointeurs avec compteur de référence, et j'en passe).

    Citation Envoyé par koala01 Voir le message
    Or, je suis désolé, mais il y a quantité de chose qui doivent n'être connues que d'un minimum d'autres choses extérieures.
    On est bien d'accord. Je te rappelle qu'une propriété est forcément publique, donc remplace purement et simplement la publication des accesseurs (qui deviennent/restent privés), OU permet une centralisation d'un concept sur un seul point d'accès (typiquement, la gestion de la taille / capacité d'un container, justement).

    Rien ne t'oblige à publier 100% de tes attributs dans des propriétés RW, ni à remplacer le concept d'attribut par celui de propriété.
    Une propriété est une interface de la classe : si quelque chose ne doit pas être manipulé de l'extérieur, pourquoi diable voudrais-tu en faire une propriété ???

    Citation Envoyé par koala01 Voir le message
    Le principe des propriétés, encore une fois, c'est que cela facilite le fait d'exposer des choses qui... auraient en tout casdu être exposées de manière beaucoup plus restrictive.
    Pas plus qu'un accesseur... C'est très exactement la même chose, seul l'utilisateur voit une différence réelle entre les deux.

    Citation Envoyé par koala01 Voir le message
    Tu sembles oublier que, lorsque tu crées un container, il ne suffit pas de modifier la valeur d'un attribut pour, effectivement, redimensionner ce container...
    Avec une propriété, si... Du moins, c'est l'impression qu'aura l'utilisateur.

    Tu me reparles de multiples fonctions de redimensionnement, moi je te parle d'un concept unique de taille (ou capacité, si tu préfères). Cela n'empêche absolument pas d'utiliser une méthode Add() pour ajouter un élément, wrappant au besoin un sous-container. Quand aux fonctions de modification de taille, il est souhaitable qu'elles existent de façon unitaire (erase(), resize(), incsize(), decsize(), que sais-je encore...) et qu'elles soient utilisées au sein de l'interface "Write" de la propriété, en fonction de la valeur passée en paramètre.

    Bref, c'est à toi de t'assurer que tu respectes ton contrat, c'est à dire que ta taille a été augmentée d'une unité après un ajout... Le formalisme utilisé (méthodes ou propriété) n'a absolument aucune importance, sauf pour l'utilisateur final qui y gagne en simplicité.

    Citation Envoyé par koala01 Voir le message
    Par contre, cela permet d'avoir des comportement clairement définis, qui ne risquent pas de prêter à confusion
    Je trouve plus confusant d'avoir N points d'accès à un concept donné (ex : la capacité d'un container) au lieu d'un seul et unique, dont la valeur ou le cadre d'utilisation (L/R-Value) est proche de la représentation "humaine" dudit concept. Une taille/capacité se lit, se détermine, peut augmenter, diminuer, être réduite à néant. C'est une formalisation assez proche de ce que l'on trouve fréquemment dans les spécifications de besoin, en fait.

    Citation Envoyé par koala01 Voir le message
    La sémantique que l'on accorde généralement à l'opérateur ()...
    [mode bash]
    Ah, la force de l'inertie...
    [/mode bash]
    Blague à part : taper dans une map de la STL se fait également via [], simplement tu n'as pas toute la finesse de contrôle qui est possible via find/insert. Et alors ? Les exceptions existent pour ça, ce sont effectivement des comportements non-nominaux de tenter de récupérer un élément inexistant et/ou d'écraser un existant, mais ça peut parfaitement convenir à la plupart des besoins.

    Pourquoi s'emm.... avec un itérateur et des méthodes find/insert, alors que l'on contrôle en amont ce qui est lu/écrit dans la map, et que les exceptions (cas anormal et rarissime) conviennent parfaitement à la gestion des erreurs ? L'opérateur [] fait parfaitement l'affaire, est cent fois plus simple à utiliser, et répond au besoin. Utiliser autre chose, c'est simplement aimer se compliquer la vie pour rien.

    Citation Envoyé par koala01 Voir le message
    Ainsi, l'opérateur [] véhicule une sémantique d'acces direct, aléatoire.
    Pour toi, peut-être. Pour moi, c'est un index, ni plus, ni moins, avec des limites hautes et basses et (par défaut) pas de "trous" dans son domaine. Son caractère aléatoire et/ou direct m'importe peu.
    Je le vois comme "méthode pratique et basique d'accès", ni plus, ni moins. Je ne pars pas du principe que c'est l'accès le plus performant (même si c'est souvent le cas), je pars du principe que c'est le plus PRATIQUE. C'est très différent.

    Notamment pour les maps, surtout quand je les utilise façon tableau associatif des langages interprétés : c'est nettement plus pratique pour la configuration lue de façon "rare" dans le programme, la configuration lue "intensivement" étant de toutes façons convertie dans un format plus efficace.

    A titre personnel, j'ai récemment écrit une classe qui possède deux opérateurs [] : le premier prends un entier en paramètre, est est effectivement LA méthode la plus efficace pour accéder à un élément donné (accès direct, aléatoire et par référence).
    L'opérateur est surchargé pour accepter, en paramètre, une chaîne de caractères. Dans ce cas précis, la recherche est séquentielle (O(N)), et sert à retrouver un élément via un critère de haut niveau, demandé de façon rare mais qui évite à l'utilisateur de se rappeler des numéros plus ou moins abstraits pour accéder aux données.
    Et moi, ça m'évite de faire une map / classe d'association dédiée à ça alors que c'est une fonction accessoire, pour ne pas dire une scorie...

    Citation Envoyé par koala01 Voir le message
    Comme je l'ai dit plus haut, tu fait l'impasse sur la sémantique, et c'est bien dommage
    Heu... On ne doit pas avoir le même sens pour sémantique (="le fond"), que j'associe en général au mot "syntaxe" (="la forme").
    La sémantique, c'est l'opération à effectuer (récupérer un élément, avoir la taille du container, etc.). La syntaxe, c'est la forme que cela prends (propriété, méthode, accesseur, service réseau, glutz vénusien...).

    Citation Envoyé par koala01 Voir le message
    sur le fait qu'une fonction doit avoir un comportement déterminé, et non un comportement susceptible de varier selon les circonstances
    Sûr... Va dire ça à la méthode connect() d'une socket, suivant si le câble réseau est branché ou pas, ou si le DHCP a filé une passerelle ou pas, ou si ça répond ou pas, etc.
    Encore heureux que les fonctions s'adaptent aux circonstances : comment voudrais-tu faire de la gestion d'erreurs sans ça ???
    Sans même parler des surcharges de méthodes...

    Je pense que tu as voulu dire autre chose que ce que je suis en train de lire : peux-tu reformuler, stp ?

    Citation Envoyé par koala01 Voir le message
    sur le fait que le meilleur moyen pour quelqu'un qui lit le code le comprenne facilement, c'est que le nom de chaque fonction représente clairement le comportement qu'elle met en oeuvre
    Ce qui est le cas d'une propriété, clairement définie comme telle bien entendu, qui regroupe via un seul identifiant un concept complet telle que la taille / capacité d'un container, au lieu de disperser cette information sur N entités diverses.
    Donc, un seul point à documenter / assimiler, et tu as l'accès à toutes les fonctions liées à un concept. Difficile de faire plus simple, plus concis et plus compréhensible, je trouve.

    Citation Envoyé par koala01 Voir le message
    sur le fait qu'il faut autant que faire se peut éviter de changer la sémantique généralement accordée à des fonctions ou des opérateurs couremment utilisés par ailleurs
    Il faut surtout éviter des présuppositions qui sont, à mon sens, dangereuses... Comme croire qu'un accès par [] est forcément direct et aléatoire, alors que la STL te prouve le contraire sur ses propres containers. Ou encore croire qu'un container nommé "Liste" est forcément lent, séquentiel et tutti-quanti, qu'une map est forcément une association complexe (une LUT n'est qu'un cas particulier de map), etc.

    Tu as aussi d'autre présuppositions malheureuses, comme de présupposer du rôle d'une fonction size() : c'est quoi, le couple (size,count), ou le couple (capacity,size) ?? En fonction des frameworks / langages / habitudes, c'est l'un, ou l'autre... Pour moi, c'est en général le premier cas par exemple, je récupère habituellement la cardinalité via count plutôt que size, si je n'ai pas de contraintes spécifiques.

    Des concepts comme les propriétés permettent de rendre les classes plus "intelligentes", sans pour autant complexifier leur interface publique, ni les rendre spécialement moins performantes, ni spécialement plus longues à écrire. Pour ma part, je trouve que ça va dans le bon sens, étant donné que développeur comme utilisateur y gagnent.


    Bon, désolé du pavé, mais t'es au moins "aussi pire" que moi au niveau longueur des posts...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  20. #220
    screetch
    Invité(e)
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Ce qui est le cas d'une propriété, clairement définie comme telle bien entendu, qui regroupe via un seul identifiant un concept complet telle que la taille / capacité d'un container, au lieu de disperser cette information sur N entités diverses.
    Donc, un seul point à documenter / assimiler, et tu as l'accès à toutes les fonctions liées à un concept. Difficile de faire plus simple, plus concis et plus compréhensible, je trouve.
    il ne s'agit pas de documenter un bidule, il s'agit que le bidule soit auto-documenté, grâce a un nom non pas "a peu près" correct mais _exact_
    resize() fait exactement ce que son nom indique.
    size non, et a besoin d'une documentation.

    operator[] n'est pas clair non plus. la preuve ? tu t'es trompé dans sa description.
    insert() est très clair lui.

Discussions similaires

  1. [langage] Je cherche un bon livre ?
    Par Anonymous dans le forum Langage
    Réponses: 13
    Dernier message: 09/04/2003, 13h16
  2. [langage] Comparer Perl avec d'autres langages comme C ?
    Par Anonymous dans le forum Langage
    Réponses: 3
    Dernier message: 10/08/2002, 23h52
  3. [langage] comment créer des fichiers ?
    Par Anonymous dans le forum Langage
    Réponses: 3
    Dernier message: 05/05/2002, 16h33
  4. Comparer des fichiers de données : Quel Langage ?
    Par Anonymous dans le forum Langages de programmation
    Réponses: 6
    Dernier message: 24/04/2002, 22h37
  5. Cours, tutoriels, logiciels, F.A.Q,... pour le langage SQL
    Par Marc Lussac dans le forum Langage SQL
    Réponses: 0
    Dernier message: 04/04/2002, 10h21

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