9
votes

Emballage des conteneurs pour maintenir la cohérence

Je me demande si c'est une bonne idée d'envelopper des conteneurs STL C ++ pour maintenir la cohérence et être capable d'échanger la mise en œuvre sans modifier le code du client.

Par exemple, dans un projet que nous utilisons Camelcase pour connaître les classes de nommage et les fonctions membres ( FOO :: Dosomething () ), je voudrais envelopper std :: Liste dans une classe comme celle-ci: xxx

Alors je serais capable d'écrire quelque chose comme ceci: xxx

Je pense que la plupart des complications modernes C ++ peuvent optimiser facilement l'indirection supplémentaire, et je pense que cette méthode a quelques-unes Avantages comme:

Je peux facilement étendre la fonctionnalité de la classe de liste. Par exemple, je veux une fonction sténographique qui trie la liste puis appelle unique () , je peux l'étendre en ajoutant une fonction de membre: xxx

En outre, je peux échanger la mise en œuvre sous-jacente (si nécessaire) sans modification du code qu'ils utilisent Liste tant que le comportement est identique. Il existe également d'autres avantages car il maintient la cohérence des conventions de dénomination dans un projet, il n'a donc pas push_back () pour stl et Bouthback () pour d'autres classes partout Le projet comme: xxx

Je me demande si cette approche présente des inconvénients majeurs (ou mineurs), ou si cela est en réalité une méthode pratique.

Ok, merci pour les réponses jusqu'à présent. Je pense que j'ai peut-être trop mettre à désigner la cohérence de la question. En réalité, la dénomination Les conventions ne sont pas ma préoccupation ici, car on peut fournir également une même interface: xxx

ou on peut même rendre l'interface d'une autre implémentation ressemble plus à la STL un que la plupart des programmeurs C ++ connaissent déjà. Mais de toute façon, à mon avis, c'est plus une chose de style et non si important du tout.

Ce qui me préoccupe est la consistance pour pouvoir modifier facilement les détails de la mise en œuvre. Une pile peut être implémentée de différentes manières: une matrice et un index supérieur, une liste liée ou même un hybride des deux, et ils ont tous la caractéristique LIFO de la structure de données. Un arbre de recherche binaire auto-équilibré peut être mis en oeuvre avec un arbre AVL ou un arbre noir rouge également, et ils ont tous deux des O (logn) Complexité de temps moyen pour la recherche, l'insertion et la suppression.

Donc, si j'ai une bibliothèque d'arbres AVL et une autre bibliothèque d'arbres noir avec différentes interfaces, et j'utilise un arbre AVL pour stocker des objets. Plus tard, j'ai pensé (utiliser des profileurs ou autre) que l'utilisation d'un arbre noir rouge donnerait un coup de pouce de performance, je devrais aller à chaque partie des fichiers qui utilisent des arbres AVL et modifieraient les noms de classe, de méthode et probablement argument commandes à ses homologues d'arbres rouges. Il y a probablement même des scénarios que la nouvelle classe n'a pas encore de fonctionnalité équivalente écrite. Je pense que cela peut également introduire également des bogues subtiles en raison des différences de mise en œuvre, ou que je fais une erreur.

Alors, ce que j'ai commencé à vous demander si cela vaut la tête pour maintenir une telle classe d'enveloppe à Cachez les détails de la mise en œuvre et fournissez une interface uniforme pour différentes implémentations: xxx

maintenant, je dois juste vous assurer que avltree :: trouve () et rbtree :: trouver () fait exactement la même chose (c.-à-d. Prendre la valeur à rechercher, renvoyer un itérateur à l'élément ou fin () , sinon). Et puis, si je veux changer d'un arbre AVL à un arbre noir rouge, tout ce que je dois faire est de changer la déclaration: xxx

à: xxx

et tout le reste sera le même, en conservant deux classes.


4 commentaires

Penser à cette idée me rend physiquement malade.


Ne voyez rien de mal à le faire. Mais voulez-vous vraiment ???


Donc, pour obtenir cette ligne droite, vous voulez faire tout cela simplement à cause de raisons de nommer et d'échanger la mise en œuvre? Naming ne pouvait pas être parce que vous pourriez alors envelopperiez beaucoup d'autres choses stl.


Vous pouvez prolonger ainsi la liste en écrivant des fonctions libres telles que Void SDUNIQUE (STD :: Liste & L) {l.sort (); l.unique ()}


7 Réponses :


7
votes

Je me demande si cette approche a des inconvénients majeurs (ou mineurs),

Deux mots: Nightmare de maintenance.

Et puis, lorsque vous recevez un nouveau compilateur C ++ activé par le déplacement, vous devrez étendre toutes vos classes d'emballage.

Ne vous méprenez pas - il n'y a rien de mal à envelopper un conteneur stl si vous avez besoin de fonctionnalités supplémentaires , mais juste pour "Noms de fonction des membres cohérents"? Trop de frais généraux. Trop de temps investi pour No Roi.

Je devrais ajouter: les conventions de dénomination incohérentes sont simplement quelque chose que vous vivez lorsque vous travaillez avec C ++. Il y a juste trop de styles différents dans des bibliothèques trop disponibles (et utiles).


0 commentaires

0
votes

Non, ne les emballez pas comme ça.
Vous enveloppez des conteneurs si vous voulez changer ce que sont les conteneurs.
Par exemple, si vous avez un ONUOMREDED_MAP qui doit seulement ajouter / supprimer des éléments en interne, il faut exposer l'opérateur [] que vous enveloppez-le et créez votre propre [] qui expose le conteneur interne [] et que vous exposez également un const_ité. const_iterator.


0 commentaires

2
votes

... Être capable d'échanger la mise en œuvre sans modifier le code client

Au niveau du code de résolution de problèmes, vous pouvez choisir de sélectionner un conteneur interne différent tout en présentant la même API. Mais, vous ne pouvez pas raisonnablement avoir du code qui choisit spécifiquement d'utiliser une liste, puis échangez la mise en œuvre à quoi que ce soit d'autre, sans compromettre les performances et les caractéristiques d'utilisation de la mémoire qui ont conduit le code client pour sélectionner une liste pour commencer. Les compromis fabriqués dans les conteneurs standard sont bien compris ... Il ne vaut pas la peine de dévaluer l'éducation de votre personnel de programmation, le matériel de référence disponible, le temps de plus en plus pour que le nouveau personnel se lève à la vitesse, etc. Juste pour avoir un wrapper maison.


0 commentaires

2
votes

Tout compilateur décent sera en mesure de faire vos classes enveloppées aussi vite que les standards, mais vos classes peuvent être seront moins utilisables dans débogueurs et d'autres outils qui auraient pu être spécialisés pour les conteneurs standards. Aussi, vous allez probablement avoir des messages d'erreur sur les erreurs de temps de compilation qui sont un peu plus cryptique que ceux déjà énigmatiques qu'un programmeur obtient en faisant des erreurs dans l'utilisation d'un modèle.

Votre convention d'appellation ne semble pas me mieux que le standard; il est en fait un peu plus mal l'OMI et un peu dangereux; par exemple. il utilise le même notationsCamel pour les deux classes et méthodes et me demande si vous savez quelles sont les limites imposées par la norme sur un nom comme _list ...

En outre votre convention de nommage est connu que par vous et probablement quelques autres, au lieu d'une norme est connue par un grand nombre de programmeurs de C (y compris vous et espérons-les quelques autres). Et que des bibliothèques externes, vous devrez peut-être ajouter à votre projet? Est-ce que vous allez changer leur interface afin que vos contenants personnalisés emballés sont utilisés au lieu de les standards?

Alors, je me demande où sont les points positifs de l'utilisation de votre convention de nommage personnalisé? Je ne vois ... downsides


2 commentaires

Pour autant que je sache, la norme C ++ ne réserve que des noms qui commencent par un soulignement dans l'espace de noms global. Lien Faire les messages d'erreur de modèle cryptique encore plus cryptique est un très bon point cependant.


C'est exactement la limitation dont je parlais. Comme je l'ai dit avant que l'utilisation soit légale mais imo dangereuse car la validité dépend du contexte. Mettre le soulignement à la fin est imo une meilleure idée.



3
votes

sonne comme un travail pour un typedef, pas une enveloppe.


1 commentaires

Entièrement d'accord. En fait, j'utilise toujours Thypef chaque fois que j'utilise un conteneur. Non seulement pour faciliter la tâche plus facile (plus facile, pas automatiquement) de modifier des conteneurs plus tard, mais également pour empêcher de répéter la définition complète STD: ... ... Plus tard et encore (surtout aux itérateurs, bien que cela soit moins problématique maintenant avec le mot-clé auto).



1
votes

Enroulez des conteneurs STL pour les faire du thread-Safe, de se cacher des détails de la mise en œuvre de l'utilisateur, de donner la fonctionnalité limitée de l'utilisateur ... Ce sont des raisons valables.

Enroulez-vous simplement parce qu'il ne suit pas votre convention de boîtier est une perte de temps et d'argent et pourrait apporter des bugs. Accepter simplement que STL utilise tous les minuscules.


0 commentaires

0
votes

En réponse à votre édition: je vous suggère de jeter un coup d'œil au modèle de conception de l'adaptateur fort>:

convertir l'interface d'une classe en Un autre client d'interface attend. Adaptateur permet aux classes travailler ensemble cela ne pouvait pas autrement à cause de interfaces incompatibles. (Concevoir Motifs, E. gamma et al.) P> blockQuote>

Ensuite, vous devez changer votre point de vue et utiliser un vocabulaire différent: considérez votre classe comme un traducteur au lieu d'un emballage et et préférez le terme "adaptateur". p>

Vous aurez une classe de base abstraite qui définit une interface commune attendue par votre application et une nouvelle sous-classe pour chaque implémentation spécifique: p> xxx pré>

vous pouvez facilement échanger Entre les implémentations possibles: p> xxx pré>

et ce modèle est évolutif: pour tester une nouvelle implémentation, fournissez une nouvelle sous-classe. Le code d'application ne change pas. P>

class NewAdapter : public ContainerInterface
{
    virtual Iterator Find(...) { /* implement new method */ }
}
delete c;
c = new NewAdapter ;
c->Find(...);


0 commentaires