2010-09-04 8 views
3

Je vais essayer de garder mon code d'échantillon très simple, mais il peut y avoir des erreurs car je le saisis sur place.Composition C++ avec itérateur

J'ai une classe appelée Téléphone.

class Phone 
{ 
public: 
    Phone(std::string manufacturer, std::string model, std::vector<Feature> features); 

private: 
    std::vector<Features> features; 
    std::string model; 
    std::string manufacturer; 
}; 

J'ai une structure nommée Feature. Comme vous pouvez le voir, un téléphone a une liste (vecteur) de caractéristiques: ie. Bluetooth, GPS, radio, etc, qui ont une catégorie: mise en réseau, navigation, multimédia.

Désormais, les informations sur les téléphones et les fonctionnalités sont stockées dans une base de données sqlite3. J'ai une fonction d'assistance qui récupérera un modèle de téléphone particulier de la base de données et retournera un objet de téléphone rempli. J'ai également une fonction qui prend en charge un objet Téléphone et écrit le téléphone dans la base de données.

Le problème est que je dois fournir au client un moyen d'itérer sur la liste des fonctionnalités d'un téléphone. Pour commencer, l'assistant de base de données doit connaître les fonctionnalités pour pouvoir les écrire dans la base de données. Deuxièmement, le client peut avoir besoin de récupérer un téléphone de la base de données et ensuite afficher la liste des fonctionnalités à l'utilisateur.

Une solution est d'ajouter les fonctions suivantes en classe Téléphone

std::vector<Feature>::iterator begin() 
std::vector<Feature>::iterator end() 

Ce n'est pas une solution idéale pour moi parce que le code client ne regardera pas intuitive - il apparaîtra comme si le client est itérez une Téléphone, quand ils sont en train d'itéter sur les fonctionnalités.

Une autre solution est discutée dans un article au http://accu.org/index.php/journals/1527 qui traite de l'utilisation d'une technique nommée "memberspaces" pour exposer les itérateurs au code client. Il en résulterait un code client plus lisible, mais la mise en œuvre est un peu désordonnée à mon avis.

Y a-t-il une meilleure solution au problème de l'itérateur, ou peut-être y a-t-il un motif de conception plus approprié que je pourrais utiliser.

Tous les commentaires seraient grandement appréciés.

+1

L'article sur les espaces membres et leur utilisation pour exposer les itérateurs est très intéressant, merci pour le lien! –

+0

Je pense qu'il me manque quelque chose. Y at-il une mauvaise partie à propos de l'itération de 'Phone :: features'? – SingleNegationElimination

Répondre

1

Dans votre cas, je pencherais pour de meilleurs prénoms:

typedef std::vector<Feature> Features; 

Features::iterator features_begin(); 
Features::iterator features_end(); 
Features::const_iterator features_begin() const; 
Features::const_iterator features_end() const; 

Exemples:
1)

// Note: you'll need to define an operator<< for Feature 
// can be used with std::algorithms 
std::copy(phone.features_begin(), phone.features_end(), 
    std::ostream_iterator<Feature>(std::cout, "\n\r"));  

2)

// Note: shamelessly borrowed from http://www.boost.org/doc/libs/1_44_0/doc/html/foreach/extensibility.html 
// add overloads of range_begin() and range_end() for Phone::Features 
inline Phone::Features::iterator range_begin(Phone& phone){ 
    return phone.features_begin(); 
} 

inline Phone::Features::iterator range_end(Phone& phone){ 
    return phone.features_end(); 
} 

namespace boost{ 
    // specialize range_mutable_iterator and range_const_iterator in namespace boost 
    template<> 
    struct range_mutable_iterator<Phone>{ 
     typedef Phone::Features::iterator type; 
    }; 

    template<> 
    struct range_const_iterator<Phone>{ 
     typedef Phone::Features::const_iterator type; 
    }; 
} 
... 
// can be used with BOOST_FOREACH 
BOOST_FOREACH(Feature x, phone){ 
    std::cout << x << std::endl; 
} 

P.S. Compte tenu de la suggestion de Jonannes et de la convention d'appellation utilisée par boost::range, les noms sont maintenant features_xxx() au lieu de xxx_features() (d'autant plus qu'ils ont plus de sens dans ce contexte).

+0

Cela ne rendrait-il pas le code incompatible avec les itérateurs boost? – Adam

+0

Pouvez-vous être plus précis? –

+0

@Adam le code source clang/llvm utilise 'features_begin()' et 'features_end()' (et 'features_size()'). J'aime cette convention de nommage et l'ai adoptée dans mes projets. –

1

Si le code client va utiliser BOOST_FOREACH, pourquoi ne pas simplement ajouter la méthode

const std::vector<Feature>& getFeatures() const; 

au téléphone?

+0

C'est une option, mais je pense que je vais simplement utiliser la convention something_begin() et something_end(), ou utiliser l'astuce memberspaces (si je veux vraiment rendre le code compatible avec boost_foreach). Quand j'ai posé la question au début, je pensais qu'il y avait peut-être une solution ultime. – Adam