|
|
 |
 |
 |
 |
design opinions requested
I have a design opinion. I have 3 models in mind, and I don't particularly like them, so maybe someone can suggest or help justify. I have a set of essentially unrelated classes (about 6). class Foo; class Bar; class Bat; I have a container-ish class that holds many of those other classes and other meta-data: class Stuff { public: vector<pair<string, Foo *> > foos; vector<pair<string, Bar *> > bars; vector<pair<string Bat *> > bats; } So here's the problem. I need to store the unrelated classes in a somewhat related way. I need to sort them all together in insert order, for later retrieval. What I mean is that later, I need to walk the container somehow and pull out al the Foos, Bars, and Bats, intermingled with each other, in the order they were inserted. So, I have dreamed up three answers, none of which I really like. 1) Make a generic base class, Stuff_Item, which is a base class for Foo, Bar, Bat, etc. Store all Foo, Bar, Bat in a single vector. Stuff_Item::stuff_type() returns and enum that callers use to determine the type of the thing held. Callers then up-cast. 2) Make a generic base class, Stuff_Item, which is not a base class, and which has methods stuff_type() and stuff_index(). stuff_type returns an enum that callers use to determine the type of the thing held. stuff_index returns an index (or an iterator) into a type- specific vector. Keep a seperate vector of Stuff_Items. Callers can then use that as a (database-style) index. 3) Make a Stuff::Item inner class, which holds an enum of pointers to Foo, Bar, Bat.. That inner class exposes is_foo(), is_bar(), is_bat() methods, and get_foo(), get_bar(), get_bat() methods. Just store one vector of Stuff::Item. Thoughs, justifications, other ideas? Thanks. Tim
On 3 Jun, 19:56, Tim H <thoc@gmail.com> wrote:
> I have a design opinion. I have 3 models in mind, and I don't > particularly like them, so maybe someone can suggest or help justify. > I have a set of essentially unrelated classes (about 6). > class Foo; > class Bar; > class Bat; > I have a container-ish class that holds many of those other classes > and other meta-data: > class Stuff { > public: > vector<pair<string, Foo *> > foos; > vector<pair<string, Bar *> > bars; > vector<pair<string Bat *> > bats; > } > So here's the problem. I need to store the unrelated classes in a > somewhat related way. I need to sort them all together in insert > order, for later retrieval. What I mean is that later, I need to walk > the container somehow and pull out al the Foos, Bars, and Bats, > intermingled with each other, in the order they were inserted. > Thoughs, justifications, other ideas?
How about this?: class Stuff { public: typedef boost::variant < std::pair<std::string, Foo *>, std::pair<std::string, Bar *>, std::pair<std::string, Bat *> > value_type; // ... private: std::vector<value_type> mixed_; };
Using this approach you can: - provide a view of the sequence that only sees pair<string, Foo *>, for example, using boost::filter_iterator in combination with the boost::get() overloads for variants. - iterate over all elements and perform a custom action depending on the type of object in each variant using boost::static_visitor I think you'll also avoid the (possibly negligible) overhead associated with virtual function calls this way, too, though I didn't check. http://www.boost.org/doc/html/variant.html Kind regards, Edd
Tim H <thoc @gmail.com> wrote: > I have a design opinion. I have 3 models in mind, and I don't > particularly like them, so maybe someone can suggest or help justify. > I have a set of essentially unrelated classes (about 6). > class Foo; > class Bar; > class Bat; > I have a container-ish class that holds many of those other classes > and other meta-data: > class Stuff { > public: > vector<pair<string, Foo *> > foos; > vector<pair<string, Bar *> > bars; > vector<pair<string Bat *> > bats; > } > So here's the problem. I need to store the unrelated classes in a > somewhat related way. I need to sort them all together in insert > order, for later retrieval. What I mean is that later, I need to walk > the container somehow and pull out al the Foos, Bars, and Bats, > intermingled with each other, in the order they were inserted. > So, I have dreamed up three answers, none of which I really like. > 1) Make a generic base class, Stuff_Item, which is a base class for > Foo, Bar, Bat, etc. Store all Foo, Bar, Bat in a single vector. > Stuff_Item::stuff_type() returns and enum that callers use to > determine the type of the thing held. Callers then up-cast. > 2) Make a generic base class, Stuff_Item, which is not a base class, > and which has methods stuff_type() and stuff_index(). stuff_type > returns an enum that callers use to determine the type of the thing > held. stuff_index returns an index (or an iterator) into a type- > specific vector. Keep a seperate vector of Stuff_Items. Callers can > then use that as a (database-style) index. > 3) Make a Stuff::Item inner class, which holds an enum of pointers to > Foo, Bar, Bat.. That inner class exposes is_foo(), is_bar(), is_bat() > methods, and get_foo(), get_bar(), get_bat() methods. Just store one > vector of Stuff::Item. > Thoughs, justifications, other ideas?
I'd have to see how the Stuff class is used to figure the best solution...
On Jun 3, 12:36 pm, "Daniel T." <danie@earthlink.net> wrote:
> Tim H <thoc @gmail.com> wrote: > > I have a design opinion. I have 3 models in mind, and I don't > > particularly like them, so maybe someone can suggest or help justify. > > I have a set of essentially unrelated classes (about 6). > > class Foo; > > class Bar; > > class Bat; > > I have a container-ish class that holds many of those other classes > > and other meta-data: > > class Stuff { > > public: > > vector<pair<string, Foo *> > foos; > > vector<pair<string, Bar *> > bars; > > vector<pair<string Bat *> > bats; > > } > > So here's the problem. I need to store the unrelated classes in a > > somewhat related way. I need to sort them all together in insert > > order, for later retrieval. What I mean is that later, I need to walk > > the container somehow and pull out al the Foos, Bars, and Bats, > > intermingled with each other, in the order they were inserted. > > So, I have dreamed up three answers, none of which I really like. > > 1) Make a generic base class, Stuff_Item, which is a base class for > > Foo, Bar, Bat, etc. Store all Foo, Bar, Bat in a single vector. > > Stuff_Item::stuff_type() returns and enum that callers use to > > determine the type of the thing held. Callers then up-cast. > > 2) Make a generic base class, Stuff_Item, which is not a base class, > > and which has methods stuff_type() and stuff_index(). stuff_type > > returns an enum that callers use to determine the type of the thing > > held. stuff_index returns an index (or an iterator) into a type- > > specific vector. Keep a seperate vector of Stuff_Items. Callers can > > then use that as a (database-style) index. > > 3) Make a Stuff::Item inner class, which holds an enum of pointers to > > Foo, Bar, Bat.. That inner class exposes is_foo(), is_bar(), is_bat() > > methods, and get_foo(), get_bar(), get_bat() methods. Just store one > > vector of Stuff::Item. > > Thoughs, justifications, other ideas? > I'd have to see how the Stuff class is used to figure the best > solution...
Most of the time Stuff will be handled in one of two patterns. for each item in stuff.big_list { if item is_foo() handle_foo() else if item is_bar() handle_bar() else handle_bat() } or for each item in stuff.big_list { if item is_foo() handle_foo } To make matters more complicated, there are actually three different Stuff containers, and each can hold a subset of the total things. For example a Stuff container can hold Foo, Bar, or Bat. A Junk container can hold Bar or Bat, but not Foo. A Mess container can hold Foo or Bat, but not bar. This extra complication is what steers me away from choices 1 and 2. The Stuff_Item class has to know about all the various things that can be stored in all the various containers. Why should an Item that goes into a Junk container know anything about Foo - Foo is not valid inside a Junk container! On th eother hand, my choice #3 ends up repeating boilerplate code in each container's inner class. Once these containers are built up, they are not going to change very often. They are going to be read fairly often.
Tim H <thoc @gmail.com> wrote: > Most of the time Stuff will be handled in one of two patterns. > for each item in stuff.big_list { > if item is_foo() > handle_foo() > else if item is_bar() > handle_bar() > else > handle_bat() > } > or > for each item in stuff.big_list { > if item is_foo() > handle_foo > }
The above looks like two perfect candidates for polymorphism. > To make matters more complicated, there are actually three different > Stuff containers, and each can hold a subset of the total things. For > example a Stuff container can hold Foo, Bar, or Bat. A Junk container > can hold Bar or Bat, but not Foo. A Mess container can hold Foo or > Bat, but not bar.
So you might end up with several different contexts in which to use "stuff". Each context should be a pure virtual class. Foo, Bar and Bat will all derive from StuffContext. Bar, and Bat will derive from JunkContext. Foo and Bat will derive from MessContext. class StuffContext { public: virtual void handleStuff() = 0; };
class Stuff { typedef vector< pair< string, StuffContext* > > Container; Container stuff; public: void handle() { for ( Container::iterator it = stuff.begin(); it != stuff.end(); ++it ) { it->second->handleStuff(); } }
};
On Jun 3, 2:22 pm, "Daniel T." <danie@earthlink.net> wrote:
> Tim H <thoc @gmail.com> wrote: > > Most of the time Stuff will be handled in one of two patterns. > > for each item in stuff.big_list { > > if item is_foo() > > handle_foo() > > else if item is_bar() > > handle_bar() > > else > > handle_bat() > > } > > or > > for each item in stuff.big_list { > > if item is_foo() > > handle_foo > > } > The above looks like two perfect candidates for polymorphism. > > To make matters more complicated, there are actually three different > > Stuff containers, and each can hold a subset of the total things. For > > example a Stuff container can hold Foo, Bar, or Bat. A Junk container > > can hold Bar or Bat, but not Foo. A Mess container can hold Foo or > > Bat, but not bar. > So you might end up with several different contexts in which to use > "stuff". Each context should be a pure virtual class. Foo, Bar and Bat > will all derive from StuffContext. Bar, and Bat will derive from > JunkContext. Foo and Bat will derive from MessContext. > class StuffContext { > public: > virtual void handleStuff() = 0; > }; > class Stuff { > typedef vector< pair< string, StuffContext* > > Container; > Container stuff; > public: > void handle() { > for ( Container::iterator it = stuff.begin(); > it != stuff.end(); > ++it ) > { > it->second->handleStuff(); > } > } > };
Two problems with this. First, handle_stuff() is up to the caller. Sometimes they just want to print a Foo, sometimes they want to do other things with it. I can't predict nor wrap that. Removing the base class method, I arrive at something similar to my #1. The second problem is that this exports policy (what can/can't be contained) from the container into the containee. I know I am being a bit difficult, I'm just trying to work through all the pros and cons :) Thanks Tim
On 3 Jun, 21:37, Tim H <thoc@gmail.com> wrote: > On Jun 3, 12:36 pm, "Daniel T." <danie @earthlink.net> wrote: > Most of the time Stuff will be handled in one of two patterns. > for each item in stuff.big_list { > if item is_foo() > handle_foo() > else if item is_bar() > handle_bar() > else > handle_bat() > }
This type switching is nasty and should be avoided. This is what the static_visitor facility of boost::variant is for. If you add any extra types to the variant that aren't supported by your static_visitors, you'll get a compiler error, rather than run-time code failing in some strange way. And if you remove types the static_visitor will still work just the same. > for each item in stuff.big_list { > if item is_foo() > handle_foo > }
The variant/filter_iterator solution handles these cases nicely. You won't even have to do explicit if-tests. That will be handled by the filter_iterator automatically. You'd just have some code like: StuffView<Foo> v(stuff); for (StuffView<Foo>::iterator it = v.begin(), e = v.end(); it != e; + +i) { // *it would yield a pair<string, Foo *>, //or just a Foo* if that's what's needed } > To make matters more complicated, there are actually three different > Stuff containers, and each can hold a subset of the total things. For > example a Stuff container can hold Foo, Bar, or Bat. A Junk container > can hold Bar or Bat, but not Foo. A Mess container can hold Foo or > Bat, but not bar. > This extra complication is what steers me away from choices 1 and 2. > The Stuff_Item class has to know about all the various things that can > be stored in all the various containers. Why should an Item that goes > into a Junk container know anything about Foo - Foo is not valid > inside a Junk container! On th eother hand, my choice #3 ends up > repeating boilerplate code in each container's inner class.
This adds no extra complication to the solution I presented. You can have a static_visitor work with a number of different variants. Additional overloads of the inner operator()s will simply be "ignored". CustomStuffVisitor v; std::for_each(stuff.begin(), stuff.end(), boost::apply_visitor(v)); Fits like a glove. Edd
> Two problems with this. First, handle_stuff() is up to the caller. > Sometimes they just want to print a Foo, sometimes they want to do > other things with it.
Each caller creates a different context. I.E., you need a different interface for each context. Previously, I assumed that Stuff, Junk and Mess were the contexts but I may have been wrong. Maybe they are simple containers. Whatever the context is, that creates an interface. > I can't predict nor wrap that.
You, of course, can predict what your code does... or at least what it *should* do. :-) > The second problem is that this exports policy (what can/can't > be contained) from the container into the containee.
If's a simple container, then Stuff probably need not exist. Just use of vector that holds objects of the approprate context. > I know I am being a bit difficult, I'm just trying to work through all > the pros and cons :)
No problem. It is hard to work through all the pros and cons when I only have partial information. However, somewhere in your code, you have: for each item in stuff.big_list { if item is_foo() handle_foo() else if item is_bar() handle_bar() else handle_bat() } The above should be: for each item in stuff.big_list { item.handle(); }
On Jun 3, 2:37 pm, e@nunswithguns.net wrote:
> On 3 Jun, 21:37, Tim H <thoc @gmail.com> wrote: > > On Jun 3, 12:36 pm, "Daniel T." <danie@earthlink.net> wrote: > > Most of the time Stuff will be handled in one of two patterns. > > for each item in stuff.big_list { > > if item is_foo() > > handle_foo() > > else if item is_bar() > > handle_bar() > > else > > handle_bat() > > } > This type switching is nasty and should be avoided. This is what the > static_visitor facility of boost::variant is for. If you add any extra > types to the variant that aren't supported by your static_visitors, > you'll get a compiler error, rather than run-time code failing in some > strange way. And if you remove types the static_visitor will still > work just the same. > > for each item in stuff.big_list { > > if item is_foo() > > handle_foo > > } > The variant/filter_iterator solution handles these cases nicely. You > won't even have to do explicit if-tests. That will be handled by the > filter_iterator automatically. > You'd just have some code like: > StuffView<Foo> v(stuff); > for (StuffView<Foo>::iterator it = v.begin(), e = v.end(); it != e; + > +i) > { > // *it would yield a pair<string, Foo *>, > //or just a Foo* if that's what's needed > } > > To make matters more complicated, there are actually three different > > Stuff containers, and each can hold a subset of the total things. For > > example a Stuff container can hold Foo, Bar, or Bat. A Junk container > > can hold Bar or Bat, but not Foo. A Mess container can hold Foo or > > Bat, but not bar. > > This extra complication is what steers me away from choices 1 and 2. > > The Stuff_Item class has to know about all the various things that can > > be stored in all the various containers. Why should an Item that goes > > into a Junk container know anything about Foo - Foo is not valid > > inside a Junk container! On th eother hand, my choice #3 ends up > > repeating boilerplate code in each container's inner class. > This adds no extra complication to the solution I presented. You can > have a static_visitor work with a number of different variants. > Additional overloads of the inner operator()s will simply be > "ignored". > CustomStuffVisitor v; > std::for_each(stuff.begin(), stuff.end(), boost::apply_visitor(v)); > Fits like a glove. > Edd
I'm going to look more into this. Thanks
On Jun 3, 3:14 pm, "Daniel T." <danie@earthlink.net> wrote: > Each caller creates a different context. I.E., you need a different > interface for each context. Previously, I assumed that Stuff, Junk and > Mess were the contexts but I may have been wrong. Maybe they are simple > containers. Whatever the context is, that creates an interface. > > I can't predict nor wrap that. > You, of course, can predict what your code does... or at least what it > *should* do. :-)
Sorry, this is a linkable library that other code will use. I can't predict :) The "containers" I have been speaking of are not as simple as I am saying, though. They actually represent nodes in a tree of a larger data structure. In fact, one of the things that these containers can contain is other containers. > > I know I am being a bit difficult, I'm just trying to work through all > > the pros and cons :) > No problem. It is hard to work through all the pros and cons when I only > have partial information. However, somewhere in your code, you have: > for each item in stuff.big_list { > if item is_foo() > handle_foo() > else if item is_bar() > handle_bar() > else > handle_bat() > }
Somewhere, that code exists, but the handle_* are undefined from my POV. > The above should be: > for each item in stuff.big_list { > item.handle(); > }
There is no common interface, except maybe <<, on these underlying objects. Handle() is a place-holder only for this example. :) Tim
On Jun 4, 8:29 am, Tim H <thoc@gmail.com> wrote:
> On Jun 3, 3:14 pm, "Daniel T." <danie @earthlink.net> wrote: > > Each caller creates a different context. I.E., you need a different > > interface for each context. Previously, I assumed that Stuff, Junk and > > Mess were the contexts but I may have been wrong. Maybe they are simple > > containers. Whatever the context is, that creates an interface. > > > I can't predict nor wrap that. > > You, of course, can predict what your code does... or at least what it > > *should* do. :-) > Sorry, this is a linkable library that other code will use. I can't > predict :) > The "containers" I have been speaking of are not as simple as I am > saying, though. They actually represent nodes in a tree of a larger > data structure. In fact, one of the things that these containers can > contain is other containers. > > > I know I am being a bit difficult, I'm just trying to work through all > > > the pros and cons :) > > No problem. It is hard to work through all the pros and cons when I only > > have partial information. However, somewhere in your code, you have: > > for each item in stuff.big_list { > > if item is_foo() > > handle_foo() > > else if item is_bar() > > handle_bar() > > else > > handle_bat() > > } > Somewhere, that code exists, but the handle_* are undefined from my > POV. > > The above should be: > > for each item in stuff.big_list { > > item.handle(); > > } > There is no common interface, except maybe <<, on these underlying > objects. Handle() is a place-holder only for this example. :) > Tim
In the class Stuff have a mapping (not std::map) which has three fields - Field one which is a counter incremented for every insert. - Field two which is an enum or int whatever you want to indicate where this stuff was added to Foo, Bar or Bat. - Field three, the index of the vector at which the the relevant Foo, bar bat was added. - Give the Stuff an interface insert(Foo*),insert(Bar*),insert(Bat*) where you increment the counter, fill in the second field for where to add Foo, Bar or Bat, and the third field the index of the vector where adding. - As for the void* get(which_type), just index the map, find the first added thing, find which thing and retrieve from the relevant vector at the vector index given by the third field of the mapping. Do an reinterpret_cast'ing depending on which_type. Haven't tried this and cannot gaurantee it will for sure certainly work. But at the face looks fine given that memory is not at premium for you. HTH. Regards, Taran
Tim H <thoc @gmail.com> wrote: > "Daniel T." <danie @earthlink.net> wrote: > > > I know I am being a bit difficult, I'm just trying to work through all > > > the pros and cons :) > > No problem. It is hard to work through all the pros and cons when I only > > have partial information. However, somewhere in your code, you have: > > for each item in stuff.big_list { > > if item is_foo() > > handle_foo() > > else if item is_bar() > > handle_bar() > > else > > handle_bat() > > } > Somewhere, that code exists, but the handle_* are undefined from my > POV.
Does the above block of code exist in your library, or is it how you want users to use your library?
On Jun 4, 6:21 am, "Daniel T." <danie@earthlink.net> wrote:
> TimH<thoc @gmail.com> wrote: > > "Daniel T." <danie @earthlink.net> wrote: > > > > I know I am being a bit difficult, I'm just trying to work through all > > > > the pros and cons :) > > > No problem. It is hard to work through all the pros and cons when I only > > > have partial information. However, somewhere in your code, you have: > > > for each item in stuff.big_list { > > > if item is_foo() > > > handle_foo() > > > else if item is_bar() > > > handle_bar() > > > else > > > handle_bat() > > > } > > Somewhere, that code exists, but the handle_* are undefined from my > > POV. > Does the above block of code exist in your library, or is it how you > want users to use your library?
It's one way to use this aspect of the library (the tree of data structs). This tree of stuff is produced once, or periodically, from a running system. Users of the library use the tree to analyze stuff. Some users want to just dump all the stuff to a file. Some users want to look for patterns. Some users want to extract parts of the stuff and turn it into other gunk. Sorry to be vague. I doubt very much anyone cares the details :)
In article <1180970444.323877.106@o11g2000prd.googlegroups.com>, Tim H <thoc@gmail.com> wrote:
> On Jun 4, 6:21 am, "Daniel T." <danie @earthlink.net> wrote: > > TimH<thoc @gmail.com> wrote: > > > "Daniel T." <danie @earthlink.net> wrote: > > > > > I know I am being a bit difficult, I'm just trying to work through all > > > > > the pros and cons :) > > > > No problem. It is hard to work through all the pros and cons when I only > > > > have partial information. However, somewhere in your code, you have: > > > > for each item in stuff.big_list { > > > > if item is_foo() > > > > handle_foo() > > > > else if item is_bar() > > > > handle_bar() > > > > else > > > > handle_bat() > > > > } > > > Somewhere, that code exists, but the handle_* are undefined from my > > > POV. > > Does the above block of code exist in your library, or is it how you > > want users to use your library? > It's one way to use this aspect of the library (the tree of data > structs). This tree of stuff is produced once, or periodically, from > a running system. Users of the library use the tree to analyze > stuff. Some users want to just dump all the stuff to a file. Some > users want to look for patterns. Some users want to extract parts of > the stuff and turn it into other gunk. > Sorry to be vague. I doubt very much anyone cares the details :)
I'm not sure why you were so vague to such a simple question though. Surely you know what you have written (or intend to write.) Is something like the above block of code in your library? Surely you know the answer to that...
On Jun 4, 9:17 am, "Daniel T." <danie@earthlink.net> wrote:
> In article <1180970444.323877.106 @o11g2000prd.googlegroups.com>, > Tim H <thoc @gmail.com> wrote: > > On Jun 4, 6:21 am, "Daniel T." <danie@earthlink.net> wrote: > > > TimH<thoc@gmail.com> wrote: > > > > "Daniel T." <danie@earthlink.net> wrote: > > > > > > I know I am being a bit difficult, I'm just trying to work through all > > > > > > the pros and cons :) > > > > > No problem. It is hard to work through all the pros and cons when I only > > > > > have partial information. However, somewhere in your code, you have: > > > > > for each item in stuff.big_list { > > > > > if item is_foo() > > > > > handle_foo() > > > > > else if item is_bar() > > > > > handle_bar() > > > > > else > > > > > handle_bat() > > > > > } > > > > Somewhere, that code exists, but the handle_* are undefined from my > > > > POV. > > > Does the above block of code exist in your library, or is it how you > > > want users to use your library? > > It's one way to use this aspect of the library (the tree of data > > structs). This tree of stuff is produced once, or periodically, from > > a running system. Users of the library use the tree to analyze > > stuff. Some users want to just dump all the stuff to a file. Some > > users want to look for patterns. Some users want to extract parts of > > the stuff and turn it into other gunk. > > Sorry to be vague. I doubt very much anyone cares the details :) > I'm not sure why you were so vague to such a simple question though. > Surely you know what you have written (or intend to write.) Is something > like the above block of code in your library? Surely you know the answer > to that...
Yes, of course. That block exists in my own users of teh library. But in each case "handle_foo()" is not exactly the same. Sometimes I just print out "Found a Foo: name = ". Sometimes I check that some sub-portion of foo works as expected. Sometimes I pass the foo to another funtion.
Tim H <thoc @gmail.com> wrote: > On Jun 4, 9:17 am, "Daniel T." <danie @earthlink.net> wrote: > > In article <1180970444.323877.106 @o11g2000prd.googlegroups.com>, > > Tim H <thoc @gmail.com> wrote: > > > On Jun 4, 6:21 am, "Daniel T." <danie@earthlink.net> wrote: > > > > TimH<thoc@gmail.com> wrote: > > > > > "Daniel T." <danie@earthlink.net> wrote: > > > > > > > I know I am being a bit difficult, I'm just trying to work > > > > > > > through all > > > > > > > the pros and cons :) > > > > > > No problem. It is hard to work through all the pros and cons when I > > > > > > only > > > > > > have partial information. However, somewhere in your code, you > > > > > > have: > > > > > > for each item in stuff.big_list { > > > > > > if item is_foo() > > > > > > handle_foo() > > > > > > else if item is_bar() > > > > > > handle_bar() > > > > > > else > > > > > > handle_bat() > > > > > > } > > > > > Somewhere, that code exists, but the handle_* are undefined from my > > > > > POV. > > > > Does the above block of code exist in your library, or is it how you > > > > want users to use your library? > > > It's one way to use this aspect of the library (the tree of data > > > structs). This tree of stuff is produced once, or periodically, from > > > a running system. Users of the library use the tree to analyze > > > stuff. Some users want to just dump all the stuff to a file. Some > > > users want to look for patterns. Some users want to extract parts of > > > the stuff and turn it into other gunk. > > > Sorry to be vague. I doubt very much anyone cares the details :) > > I'm not sure why you were so vague to such a simple question though. > > Surely you know what you have written (or intend to write.) Is something > > like the above block of code in your library? Surely you know the answer > > to that... > Yes, of course. That block exists in my own users of teh library. > But in each case "handle_foo()" is not exactly the same.
Then your library follows more of a representational paradigm rather than an OO paradigm so the Visitor pattern is appropriate. You might also want to check out the Acyclic Visitor pattern.
|
 |
 |
 |
 |
|