2008-09-24 6 views
0

Je suis en train de concevoir une classe qui stocke (met en cache) un ensemble de données. Je veux rechercher une valeur, si la classe contient la valeur puis l'utiliser et modifier une propriété de la classe. Je suis préoccupé par la conception de l'interface publique.
Voici comment la classe va être utilisé:Meilleure conception pour la méthode de recherche et de modification possible

 
ClassItem *pClassItem = myClass.Lookup(value); 
if (pClassItem) 
{ // item is found in class so modify and use it 
    pClassItem->SetAttribute(something); 
    ... // use myClass 
} 
else 
{ // value doesn't exist in the class so add it 
    myClass.Add(value, something); 
} 

Cependant, je ne veux pas avoir à exposer CLASSITEM à ce client (CLASSITEM est un détail de mise en œuvre de MyClass). pour contourner que les éléments suivants pourraient être envisagées:

 
bool found = myClass.Lookup(value); 
if (found) 
{ // item is found in class so modify and use it 
    myClass.ModifyAttribute(value, something); 
    ... // use myClass 
} 
else 
{ // value doesn't exist in the class so add it 
    myClass.Add(value, something); 
} 

Toutefois, elle est inefficace Modifier devra recommencer la recherche. Cela suggère un type lookupAndModify de la méthode:

 
bool found = myClass.LookupAndModify(value, something); 
if (found) 
{ // item is found in class 
    ... // use myClass 
} 
else 
{ // value doesn't exist in the class so add it 
    myClass.Add(value, something); 
} 

Mais rouler LookupAndModify dans une méthode qui semble être la conception très pauvre. Il ne modifie également que si la valeur est trouvée et donc le nom est non seulement lourd mais trompeur aussi bien.

Y a-t-il un autre meilleur design qui contourne ce problème? Tous les modèles de conception pour cela (je ne pouvais rien trouver par google)?

Répondre

1

Deux choses.

La première solution est proche. Cependant, ne renvoyez pas ClassItem *. Renvoie un "objet opaque". Un index entier ou un autre code de hachage opaque (sans signification) pour le client, mais utilisable par l'instance myClass.

Ensuite, la recherche renvoie un index, que modifier peut ensuite utiliser.

void *index = myClass.lookup(value); 
if(index) { 
    myClass.modify(index, value); 
} 
else { 
    myClass.add(value); 
} 

Après avoir écrit le « primitif » de recherche, modifier et d'ajouter, puis d'écrire vos propres opérations composites construites autour de ces primitives.

Écrivez un LookupAndModify, TryModify, AddIfNotExists et d'autres méthodes construites à partir de vos pièces de niveau inférieur.

+0

C'est quelque chose que j'ai d'abord considéré mais que j'ai oublié quand j'ai posté ma question. Je n'aime pas beaucoup utiliser des objets opaques car la sécurité de type devient un problème mais je suppose que tant que la classe est soigneusement construite c'est OK. – danio

+0

Les objets opaques peuvent facilement être protégés. Vous n'avez pas besoin d'utiliser void *, vous pouvez utiliser MySecretThing * à la place. MySecretThing peut contenir beaucoup de choses privées ou un vide *. –

1

Cela suppose que vous définissez la valeur à la même « quelque chose » dans les deux cas modifier et d'ajouter:

if (!myClass.AddIfNotExists(value, something)) { 
    // use myClass 
} 

Sinon:

if (myClass.TryModify(value, something)) { 
    // use myClass 
} else { 
    myClass.Add(value, otherSomething); 
} 
+0

Merci d'avoir répondu.Bien que vos noms soient meilleurs, je pense que TryModify et AddIfNotExists souffrent de la même faiblesse que LookupAndModify - ils font tous trois choses (recherche, éventuellement modifier, retourner trouvé) ce qui semble trop pour une méthode pour moi. – danio

+0

Cela dépend de la sémantique de la classe - que je ne peux pas discerner des échantillons de code génériques. C'est bien (et même préférable dans beaucoup de cas) de concevoir une interface grossière optimisée pour l'utilisation du client. Comment votre classe fait le travail (peut-être qu'il a 3 méthodes internes) est un détail de mise en œuvre. –

2

En fait std::set<>::insert() fait précisément cela. Si la valeur existe, elle renvoie l'itérateur pointant vers l'élément existant. Sinon, l'itérateur dans lequel l'insertion a été effectuée est renvoyé.

Il est probable que vous utilisez une structure de données similaire pour les recherches rapides de toute façon, une interface publique propre (site appelant) sera:

myClass.SetAttribute(value, something) 

qui fait toujours la bonne chose. MyClass gère la plomberie interne et les clients ne s'inquiètent pas de savoir si la valeur existe.

+0

Si vous regardez les cas d'utilisation fournis, ils aboutissent tous au même résultat: l'objet est modifié et stocké. Donc, pourquoi (à ce niveau) exposer et compliquer l'interface en obligeant le client à faire les deux vérifications à chaque fois. –

+0

Je supposais que // utiliser myClass était un indice que quelque chose de * différent * se produisait si la valeur existait déjà. –

Questions connexes