Question

Dans l'excellent livre (autrement) excellent, Normes de codage C ++ , article 44, intitulée "Préférer écrire des fonctions non membres non amies" , Sutter et Alexandrescu recommandent que seules les fonctions ayant réellement besoin d'un accès aux membres d'une classe soient elles-mêmes membres de cette classe. Toutes les autres opérations pouvant être écrites en utilisant uniquement des fonctions membres ne doivent pas faire partie de la classe. Ils devraient être non-membres et non-amis. Les arguments sont les suivants:

  • Cela favorise l'encapsulation, car il y a moins de code qui nécessite un accès aux éléments internes d'une classe.
  • Cela facilite la rédaction de modèles de fonctions, car vous n'avez pas à deviner à chaque fois si une fonction est membre ou non.
  • La classe reste petite, ce qui facilite les tests et la maintenance.

Même si je vois la valeur de cet argument, je vois un inconvénient majeur: mon IDE ne peut pas m'aider à trouver ces fonctions! Chaque fois que j'ai un objet quelconque et que je souhaite voir quelles opérations sont disponibles dessus, je ne peux pas simplement taper " pMysteriousObject- > " et obtenir une liste de fonctions membres plus.

Garder un design épuré, c'est finalement faciliter votre vie de programmation. Mais cela rendrait effectivement le mien beaucoup plus difficile.

Je me demande donc si cela en vaut vraiment la peine. Comment gérez-vous cela?

Était-ce utile?

La solution

Je vais devoir être en désaccord avec Sutter et Alexandrescu sur celui-ci. Je pense que si le comportement de la fonction foo () relève du domaine des responsabilités de la classe Bar , alors foo () devrait faire partie de bar () .

Le fait que foo () ne nécessite pas d'accès direct aux données des membres de Bar ne signifie pas qu'il ne fait pas partie conceptuellement de la barre . . Cela peut également signifier que le code est bien pris en compte. Il n’est pas rare d’avoir des fonctions membres qui exécutent tout leur comportement via d’autres fonctions membres, et je ne vois pas pourquoi cela devrait être le cas.

Je suis tout à fait d'accord avec le fait que les fonctions liées à la périphérie ne devraient pas faire partie de la classe, mais si quelque chose est au cœur des responsabilités de la classe, il n'y a aucune raison pour qu'elle ne soit pas membre, peu importe si c’est directement avec les données des membres.

En ce qui concerne ces points spécifiques:

  

Il favorise l'encapsulation, car il y a moins de code qui nécessite un accès aux éléments internes d'une classe.

En effet, moins il y a de fonctions qui accèdent directement aux internes, mieux c'est. Cela signifie que faire en sorte que les fonctions membres fassent autant que possible via d'autres fonctions membres est une bonne chose. Si vous séparez des fonctions bien factorisées de la classe, vous ne disposez que d'une demi-classe, ce qui nécessite un ensemble de fonctions externes pour être utiles. Retirer les fonctions bien factorisées de leurs classes semble également décourager l’écriture de fonctions bien factorisées.

  

Cela facilite la rédaction de modèles de fonctions, car vous n'avez pas à deviner à chaque fois si une fonction est membre ou non.

Je ne comprends pas du tout. Si vous retirez un tas de fonctions des classes, vous aurez davantage de responsabilités dans les modèles de fonctions. Ils sont obligés de supposer que encore moins les fonctionnalités sont fournies par les arguments de leur modèle de classe, sauf si nous supposons que la plupart des fonctions extraites de leurs classes vont être converties en un modèle (ugh).

  

La classe reste petite, ce qui facilite les tests et la maintenance.

Euh, bien sûr. Il crée également de nombreuses fonctions externes supplémentaires à tester et à gérer. Je ne parviens pas à voir la valeur en cela.

Autres conseils

Scott Meyers a un avis similaire à celui de Sutter. Voir ici .

Il déclare également clairement ce qui suit:

"En se basant sur son travail avec différentes classes ressemblant à des chaînes, Jack Reeves a observé que certaines fonctions ne" se sentent "pas" droit quand ils sont faits non membres, même s’ils peuvent être non membres non amis. Le "meilleur" Pour une classe, l’interface ne peut être trouvée qu’en équilibrant de nombreuses préoccupations concurrentes, dont le degré d’encapsulation n’est qu’un. "

Si une fonction est quelque chose qui "a tout simplement du sens" pour être une fonction membre, faites-en une. De même, si cela ne fait pas vraiment partie de l'interface principale, et que "cela a du sens" être un non-membre, faites-le.

Remarque: avec les versions surchargées de l'opérateur == (), par exemple, la syntaxe reste la même. Donc, dans ce cas, vous n’avez aucune raison, de ne pas , d’en faire une fonction flottante non membre non membre déclarée au même endroit que la classe, sauf si elle a vraiment besoin de l’accès à des membres privés (dans mon expérience, sera rarement). Et même alors, vous pouvez définir l'opérateur! = () Un non-membre et en termes d'opérateur == ().

Je ne pense pas qu'il serait faux de dire qu'entre eux, Sutter, Alexandrescu et Meyers ont fait plus pour la qualité du C ++ que quiconque.

Ils posent une question simple:

  

Si une fonction d'utilité a deux classes indépendantes comme paramètre, quelle classe devrait "posséder" la fonction membre?

Un autre problème est que vous ne pouvez ajouter des fonctions membres que lorsque la classe en question est sous votre contrôle. Toutes les fonctions d'assistance écrites pour std :: string devront être non-membres, car vous ne pouvez pas rouvrir la définition de la classe.

Pour ces deux exemples, votre IDE fournira des informations incomplètes et vous devrez utiliser la méthode "à l'ancienne".

Étant donné que les experts C ++ les plus influents au monde considèrent que les fonctions non membres avec un paramètre de classe font partie de l'interface de classes, il s'agit davantage d'un problème lié à votre IDE que du style de codage.

Votre IDE changera probablement dans une version ou deux, et vous pourrez même les amener à ajouter cette fonctionnalité. Si vous modifiez votre style de codage pour l'adapter à l'IDE d'aujourd'hui, vous constaterez peut-être que, dans le futur, vous rencontrerez de plus gros problèmes avec un code non extensible / non maintenable.

Il est vrai que les fonctions externes ne doivent pas faire partie de l'interface. En théorie, votre classe ne devrait contenir que les données et exposer l'interface pour ce à quoi elle est destinée et non pour les fonctions utilitaires. L'ajout de fonctions utilitaires à l'interface agrandit simplement la base de code de classe et la rend moins gérable. Je maintiens actuellement une classe avec environ 50 méthodes publiques, ce qui est insensé.

Maintenant, en réalité, je conviens que ce n’est pas facile à appliquer. Il est souvent plus simple d'ajouter une autre méthode à votre classe, encore plus si vous utilisez un environnement de développement intégré (IDE) capable d'ajouter simplement une nouvelle méthode à une classe existante.

Afin de garder mes classes simples et de pouvoir toujours centraliser une fonction externe, j’utilise souvent une classe d’utilitaire qui fonctionne avec ma classe, voire avec des espaces de noms. Je commence par créer la classe qui encapsulera mes données et exposera l'interface la plus simple possible. Je crée ensuite une nouvelle classe pour chaque tâche que je dois faire avec la classe.

Exemple: créez une classe Point, puis ajoutez une classe PointDrawer pour l’attribuer à un bitmap, PointSerializer pour l’enregistrer, etc.

Si vous leur donnez un préfixe commun, votre IDE vous aidera peut-être si vous tapez

::prefix

ou

namespace::prefix

Dans de nombreuses langues OOP, les méthodes sans amis et sans classe sont des citoyens de troisième classe résidant dans un orphelinat sans aucune connexion. Quand j'écris une méthode, j'aime bien choisir de bons parents - un cours approprié - où ils ont les meilleures chances de se sentir les bienvenus et de les aider.

J'aurais pensé que l'IDE vous aidait réellement.

L'EDI masque les fonctions protégées de la liste, car elles ne sont pas disponibles pour le public , contrairement au concepteur de la classe. .

Si vous aviez été dans la portée de la classe et avez tapé this- > , les fonctions protégées seraient affichées dans la liste.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top