I'll try and keep my sample code very straightforward, but it may have errors as I'm typing it on the spot.
I have a class named 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;
};
I have a struct named Feature.
struct Feature
{
Feature(std::string feature, std::string category);
std::string feature;
std::string category;
};
As you can see a phone has a list (vector) of features: ie. bluetooth, GPS, radio, etc, which have a category: networking, navigation, multimedia.
Now information about phones and features are stored in a sqlite3 database. I have a helper function which will retrieve a particular phone model from the database and return a populated Phone object. I also have a function which takes in a Phone object and writes the Phone to the database.
The problem is I need to provide the cl开发者_开发知识库ient some way of iterating over the list of Features of a Phone. For starters the database helper needs to know about the features so it can write them to the database. Second the client may need to retrieve a Phone from the database and then display the list of features to the user.
One solution is to add the following functions in class Phone
std::vector<Feature>::iterator begin()
std::vector<Feature>::iterator end()
This is not an ideal solution for me because the client code will not look intuitive - it will appear as if the client is iterating over a Phone, when they are actually iterating over Features.
Another solution is discussed in an article at http://accu.org/index.php/journals/1527 which discusses using a technique named "memberspaces" to expose iterators to client code. It would result in more readable client code, but the implementation is a bit messy in my opinion.
Is there a better solution to the iterator problem, or maybe there is a more suitable design pattern I could be using.
Any feedback would be greatly appreciated.
In your case I would go for better names first:
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;
Examples:
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. Given Jonannes' suggestion and the naming convention used by boost::range
the names are now features_xxx()
instead of xxx_features()
(especially since they do make more sense in this context).
If the client code is going to use BOOST_FOREACH, why not just add the method
const std::vector<Feature>& getFeatures() const;
to Phone?
精彩评论