Sign in to follow this  

What OOP pattern to use?

This topic is 4598 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I'm fairly new to C++ OOP, so I need help decided if/what pattern to use for my situation. This is probably very easy, and my brain is just cloudy. I have class A. Class A needs to perform operations on class X, so it has an aggregation relationship. Now, class Y and Z perform the same operations as class X, and class A may want to use Y or Z instead. Okay, easy solution. You have interface I that X, Y, and Z inherit from. Class A then has a member variable of type interface I. And then calls I->foo(). But, what if class A may need to call X and Y, or X and Z, or all three? The only way I can think of is to have a bit-field that has USE_X USE_Y and USE_Z bits. Then create an aggregate member variable of each class type X, Y, and Z. Then do an if on USES field against each bit, if true, call X->foo(), Y->foo(), or z->foo(). But there has to be a more OO way to do this. Unfortunately my GoF book isn't with me (of which I have read very little, which I now regret.) Thanks!

Share this post


Link to post
Share on other sites
Your post is a bit confusing, but if I understand what you are trying to do correctly, then I think you want something like this: X, Y, and Z inherit from I. Each instance of class A has a reference/pointer/handle (depending on the language) to an object that implements interface I. Each instance of A can then make use of X, Y, or Z simply by making the pointer point to an object of the appropriate type. If that is not what you are looking for, you will probably have to be clearer about what your problem is, as I can read your post several different ways. Hope that helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by Chris81
Hello,

I'm fairly new to C++ OOP, so I need help decided if/what pattern to use for my situation. This is probably very easy, and my brain is just cloudy.

I have class A. Class A needs to perform operations on class X, so it has an aggregation relationship. Now, class Y and Z perform the same operations as class X, and class A may want to use Y or Z instead.

Okay, easy solution.

You have interface I that X, Y, and Z inherit from. Class A then has a member variable of type interface I. And then calls I->foo().

But, what if class A may need to call X and Y, or X and Z, or all three?

Will it? That's an odd thing for a class to be required to do, so I would double check your logic first.
Quote:

The only way I can think of is to have a bit-field that has USE_X USE_Y and USE_Z bits. Then create an aggregate member variable of each class type X, Y, and Z. Then do an if on USES field against each bit, if true, call X->foo(), Y->foo(), or z->foo().

But there has to be a more OO way to do this. Unfortunately my GoF book isn't with me (of which I have read very little, which I now regret.)

Thanks!


There is a more OO way to do this, and you should definantly look in GoF (it has at least one of the possible ways to do this). But, like I said, evaluate your logic first to decide if it will ever actually need to call upon all three.

Share this post


Link to post
Share on other sites
mumpo: sorry, I'll try to elaborate more clearly

washu: I'm pretty sure I'm using sound logic in this case, but you tell me.

Here is an example of what I'm trying to do.

I have a CFarmer class. The CFarmer class will have lots of operations such as FeedAnimals(). Then I have classes such as CCow, CChicken, and CDog. When I create a CFarmer class, I'm going to want to say CFarmer->SetAnimals( COW & CHICKEN ). Then when I call CFarmer->FeedAnimals() it may do some common stuff first like PutOnOveralls(), then call CCow->Feed() and CChicken->Feed() because COW & CHICKEN bits were set. Then CCows feed() may involve completely different steps than CChickens feed(), such as what type of food and the preparation process.

As you can see, I dont know which or how many of the common animal objects will be called until runtime.

Pretty stupid example, but I'm corny so it fits, heh.

Share this post


Link to post
Share on other sites
Ok, well, you don't need flags it would be more like this:


abstract class Animal { public void Feed(); }
class Chicken : Animal {
public override void Feed() {}
}
class Cow : Animal {
public override void Feed() {}
}
class Farmer {
public void Add(Animal a) { animals.Add(a); }
public void Feed() {
foreach(Animal a in animals) {
a.Feed();
}
}
}

Farmer f = new Farmer();
f.Add(new Chicken());
f.Add(new Cow());
f.Feed();


Note the example is in C#, but the principle applies to any object oriented language.

Share this post


Link to post
Share on other sites

class Animal
{
public:
virtual Feed();
};
class Chicken : public Animal
{
public:
virtual Feed() { GiveGrain(); }
}
class Farmer
{
std::vector<Animal*> AnimalsToFeed;
public:
void FeedAnimals()
{
std::vector<Animal*>::iterator begin, end;
begin = AnimalsToFeed.begin();
end = AnimalsToFeed.end();
while(begin < end)
{
(*begin)->Feed();
++begin;
}
}
}





No matter what animal is, he'll feed it.

Edit: Washu beat me...

Share this post


Link to post
Share on other sites
Hmm, well that's easy enough. However, there will be lots of operations on the animals, and there will be lots of places in the farmers logic that will call these operations. If everytime I need to call an animal operation I have to write a loop, that could get ugly, wouldn't it?

Share this post


Link to post
Share on other sites
If each animal will deal with each action differently, then they will need to have functions to perform the action anyways. If they share behavior, then you can move it to the base class.

Share this post


Link to post
Share on other sites
Yeah, they will, the objects will always share a type of action, just go about it different ways. But I'm not quite sure how that applies to having to write lots of loops.

If I use an array of animals, then every place I call the "shared logic" functions I will have to write a loop. If this is a large class then that will be a lot of loops. Still seems there shoud be a more OO way of doing this, don't you think?

Share this post


Link to post
Share on other sites
Quote:
Original post by Chris81
I just thought of a GoF pattern I remember reading the synopsis of in the front of the book cover - Chain of Command pattern? Would that fit?


Such as maybe this? If I remember correctly how that is used, I would have something like a chain of animals.

Then AddAnimal( CCow ) would add the cow to the chain, and so on.

Then Farmer would call HeadAnimalChain->Feed(). Which would be the first animal added, which would then call NextAnimal->Feed() to goto the next animal, and so on down the chain.

No more writing loops.

Share this post


Link to post
Share on other sites
When you're going to do something to multiple things, you need to do a loop. You can cut down on the loop appearances with std::for_each, but then you need a class with an overloaded operator()...

The OO in this is that each thing does something different, but you only do one function call on each.

Share this post


Link to post
Share on other sites
I have not studied formal design, or pattering. Further I am not an expert. Reader Beware.


#include <iostream>
#include <vector>

using namespace std;

struct xyz_virtual_interface{
//
// Make interfaces to the common functions in X,Y, and Z
//
virtual void doa()=0;
virtual ~xyz_virtual_interface(){}
};


struct X : public xyz_virtual_interface{

void doa(){ cout << "X"; }
};


struct Y : public xyz_virtual_interface{

void doa(){ cout << "Y"; }
};


struct Z : public xyz_virtual_interface{

void doa(){ cout << "Z"; }
};


class xyz_exec_list{
//
//
//
private:
vector<xyz_virtual_interface *> exec_list;
typedef vector<xyz_virtual_interface *> shortname;
public:
void doa();
void add(xyz_virtual_interface *);

xyz_exec_list(){}
~xyz_exec_list();
};



void xyz_exec_list::doa(){
//
// Exec all stored in the list.
//
// TODO: replace with for_each.
shortname::iterator elit;

for (elit=exec_list.begin();elit!=exec_list.end();++elit){
(*elit)->doa();
}
}


void xyz_exec_list::add(xyz_virtual_interface *in){
//
// Add functor to the list. The list takes ownership.
//
exec_list.push_back(in);
}



xyz_exec_list::~xyz_exec_list(){
//
// Delete interfaces out of the internal vector.
//
shortname::iterator elit;

for (elit=exec_list.begin();elit!=exec_list.end();++elit){
delete (*elit);
}
}

struct A{
xyz_exec_list xyz;
void doa(){ xyz.doa(); }
};


int main(){

A a,b;

a.xyz.add(new X());
b.xyz.add(new Y());
b.xyz.add(new Z());
b.xyz.add(new Y());

cout << "a: ";
a.doa();
cout << "\nb: ";
b.doa();
cout << "\n";
}




And this isn't really the best way to go about it [definately not, depending on what you're doing...] but it's a better way than a bit field... Oh, and most folks will tell you to use smart pointers rather than "giving" the pointer to the xyz_exec_list. I am alas, quite ignorant of their syntax, but the idea is correct.

[edit: Really, really too slow...]

Share this post


Link to post
Share on other sites
Telastyn: Thanks for creating that example, that is interesting. It definately adds to their looping a vector idea. With yours it abstracts the loop into a controller class so I only write the loop once and can call it several times.

However, it still has the limitation of having to create a controller class, which isn't a big deal, but having to write a controller function for every function is a little inconvenient.

I'm thinking about how to make a CChain<IAnimal> class, however I'm trying to figure out a way to make it non-intrusive, which I doubt will happen. Each class will have to have a pointer to the next object, then call nextObject->foo() at the end of every foo().

Share this post


Link to post
Share on other sites
Quote:
Original post by Chris81


However, it still has the limitation of having to create a controller class, which isn't a big deal, but having to write a controller function for every function is a little inconvenient.



You don't have to!

[concrete example forthcoming...]

[edit: concrete example!]

Now, this is probably going to be horrific to people who know more than I. Further, I'm fairly certain pre-made libraries such as boost::function will provide cooler stuff that just works better.

This though should provide plenty of details to see what cool stuff functors and templates can do.


#include <iostream>
#include <vector>

using namespace std;

struct xyz_virtual_interface{
//
// Make interfaces to the common functions in X,Y, and Z
//
virtual void operator()()=0;
virtual ~xyz_virtual_interface(){}
};


template <typename F>
struct xyz_concrete_global_interface : public xyz_virtual_interface{
F f;
void operator()(){
f();
}
xyz_concrete_global_interface(F inf):f(inf){}
};


template <typename T, typename F>
struct xyz_concrete_member_interface : public xyz_virtual_interface{
T t;
F f;
void operator()(){
(t.*f)();
}
xyz_concrete_member_interface(F inf):f(inf){}
};


template <typename F>
xyz_virtual_interface *make_vi(F f){

return(new xyz_concrete_global_interface<F>(f));
}


template <typename T, typename F>
xyz_virtual_interface *make_vi(T t, F f){

return(new xyz_concrete_member_interface<T,F>(f));
}


struct X {

void operator()(){ cout << "X"; }
};


struct Y {

void uc(){ cout << "Y"; }
void lc(){ cout << "y"; }
};


void Z(){
cout << "Z";
};


class xyz_exec_list{
//
//
//
private:
vector<xyz_virtual_interface *> exec_list;
typedef vector<xyz_virtual_interface *> shortname;
public:
void doa();
void add(xyz_virtual_interface *);

xyz_exec_list(){}
~xyz_exec_list();
};



void xyz_exec_list::doa(){
//
// Exec all stored in the list.
//
// TODO: replace with for_each.
//
shortname::iterator elit;
xyz_virtual_interface *xyzvit;

for (elit=exec_list.begin();elit!=exec_list.end();++elit){
xyzvit=*elit;
(*xyzvit)();
}
}


void xyz_exec_list::add(xyz_virtual_interface *in){
//
// Add functor to the list. The list takes ownership.
//
exec_list.push_back(in);
}


xyz_exec_list::~xyz_exec_list(){
//
// Delete interfaces out of the internal vector.
//
shortname::iterator elit;

for (elit=exec_list.begin();elit!=exec_list.end();++elit){
delete (*elit);
}
}

struct A{
xyz_exec_list xyz;
void doa(){ xyz.doa(); }
};


int main(){

A a,b;

a.xyz.add(make_vi(X()));
b.xyz.add(make_vi(Y(),&Y::lc));
b.xyz.add(make_vi(Z));
b.xyz.add(make_vi(Y(),&Y::uc));

cout << "a: ";
a.doa();
cout << "\nb: ";
b.doa();
cout << "\n";
}



and the output:

> ./xyz2
a: X
b: yZY




[Edited by - Telastyn on May 16, 2005 11:51:29 PM]

Share this post


Link to post
Share on other sites
Patterns are great. They tell you about relationships and behaviours that some people have spotted which appear again and again in their own programs.

So patterns come from reflection upon existing programs.

You can sometimes have code which you can see is 'almost like' a certain pattern. It might be a good idea to tidy it up to make it a firmer example of the pattern.

Also, what you really want to be doing is generally moving towards the patterns even if you don't make a complete implementation of the pattern.

It sounds as though you have Analysis Paralysis, with your 'what ifs' and so on. A good tactic to take is to just get on and write some code. Do the minimal you need to get something working. Just get him feeding the chickens. Then worry about the cows. You'll find yourself copying and pasting code and think 'there must be a better way than this'. Then you can change things. You might even see a pattern developing!

Refactoring - Changing the Design of Existing Code is an excellent book, which I thoroughly recommend.

Share this post


Link to post
Share on other sites
Writing some sort of chaining system here is serious overkill, not to mention extremely difficult in C++ given what you intend. You make the system much more fragile in the name of removing 1 line of looping code. If it makes you feel any better, the GOF don't have a problem with loops, and neither should you.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Kylotan
Writing some sort of chaining system here is serious overkill, not to mention extremely difficult in C++ given what you intend. You make the system much more fragile in the name of removing 1 line of looping code. If it makes you feel any better, the GOF don't have a problem with loops, and neither should you.


I didn't think it was very difficult to implement. I sat down at 10:00pm tonight and started the following code, and finished testing it at 10:50pm. However, just because I implemented it and it seems to test okay, doesn't mean there isn't a major flaw with putting this into practice, as I'm not experienced enough in C++ to know that yet. What do you think of it?

CChain.h and CChainLink.h:

#ifndef CCHAIN_H
#define CCHAIN_H

#define NEXT_LINK( a ) if ( GetNextLink() ) GetNextLink()->##a;

template <class T>
class CChain
{
public:
CChain() : m_headLink(NULL), m_tailLink(NULL) { }

void AddLink(T *link);
void RemoveLink(T *link);
inline T *GetHead() { return m_headLink; }
inline T *GetTail() { return m_tailLink; }

inline T *operator ->() { return m_headLink; }
private:
T *m_headLink;
T *m_tailLink;
};

template <class T>
void CChain<T>::AddLink(T *link)
{
if ( m_headLink == NULL )
{
m_headLink = link;
m_tailLink = link;
return;
}

m_tailLink->SetNextLink(link);
m_tailLink = link;
}

template <class T>
void CChain<T>::RemoveLink(T *link)
{
T *last;
T *cur = last = m_headLink;
while ( cur )
{
if ( cur == link )
{
if ( cur == m_headLink )
{
m_headLink = link->GetNextLink();
}
else if ( cur == m_tailLink )
{
m_tailLink = last;
last->SetNextLink( NULL );
}
else
{
last->SetNextLink( link->GetNextLink() );
}
break;
}
last = cur;
cur = cur->GetNextLink();
}
}

#endif

#ifndef ICHAINLINK_H
#define ICHAINLINK_H

template <class T>
class IChainLink
{
public:
IChainLink() : m_nextLink(NULL) { }

inline void SetNextLink(T *next) { m_nextLink = next; }
inline T *GetNextLink() { return m_nextLink; }
private:
T *m_nextLink;
};

#endif



Test objects:

void Msg( const char *msg )
{
MessageBox(hWnd_Main, msg, "TEST", MB_OK);
}

class ICommon : public IChainLink<ICommon>
{
public:
virtual void Foo() = 0;
};

class A : public ICommon
{
public:
void Foo()
{
Msg( "A - Foo()" );

NEXT_LINK( Foo() );
}
};

class B : public ICommon
{
public:
void Foo()
{
Msg( "B - Foo()" );

NEXT_LINK( Foo() );
}
};

class C : public ICommon
{
public:
void Foo()
{
Msg( "C - Foo()" );

NEXT_LINK( Foo() );
}
};



Testing it out after windows initialization:

CChain<ICommon> chain;
A a;
B b;
C c;
chain.AddLink( &b );
chain.AddLink( &a );
chain.AddLink( &c );

chain->Foo();

chain.RemoveLink( &a );

chain->Foo();



After running this, I get the message boxes
B - Foo()
A - Foo()
C - Foo()
because that's the order I added them to the chain.
Then after removing a from the chain, I naturally get:
B - Foo()
C - Foo()

Please note that the chain doesn't delete the pointers when removed from the link, so that's still up to the programmer to manage memory. In the example I'm using the stack to do it, but when I implement it the objects will already be handled by a resource manager.

If you see any holes that I'm missing in this, please let me know, thanks.

Share this post


Link to post
Share on other sites
Why may I ask are you using a templated container class rather than the Standard Library's provided ones? You don't seem to be gaining anything but more code to write/debug/design.

Further, inappropriate use of IChainLink::SetNextLink() will result in a memory leak. It would be modestly better I think if that were private [or protected at least] and CChain made a friend of IChainLink.

And binding objects to their container like that is just icky.

[edit: I suppose 'icky' isn't too helpful...

It is "better" to have the individual objects be independant of their container. It allows for you to more easily change your design should requirements change. It allows you to reuse the objects in other code where the container might be subtly different. It allows you to more easily reuse different objects in this container without putting NEXT_LINK( function() )'s everywhere. It allows you to execute a subset of the container rather than always executing everything. It allows you to design in variable conditions on which to execute the objects... ]

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Why may I ask are you using a templated container class rather than the Standard Library's provided ones? You don't seem to be gaining anything but more code to write/debug/design.


It's the only way that I know of to accomplish the design goal. If I used a STL container, the "link" wouldn't know where it was at in the "chain" and thus couldn't call the next link's function.

Quote:

Further, inappropriate use of IChainLink::SetNextLink() will result in a memory leak. It would be modestly better I think if that were private [or protected at least] and CChain made a friend of IChainLink.

Good point, thanks. I'm not sure how it would result in a memory leak, as the container is independent of the memory clean-up process as I described before. You're just passing pointers to the container, it's your responsibility to clean up the objects the pointers point to. In the example, the stack does it. In practice, ICommon or one of it's ancestors would alredy be bound to a resource management class, or you would manually clean up after yourself. Thanks again though, this is exactly the type of feedback I was hoping to get and that is still a good point because the way I had it was not properly encapsulated.

Quote:

And binding objects to their container like that is just icky.

See answer to your first question...also explains why it would have to be bound to the container.

Quote:

[edit: I suppose 'icky' isn't too helpful...

It is "better" to have the individual objects be independant of their container. It allows for you to more easily change your design should requirements change. It allows you to reuse the objects in other code where the container might be subtly different. It allows you to more easily reuse different objects in this container without putting NEXT_LINK( function() )'s everywhere. It allows you to execute a subset of the container rather than always executing everything. It allows you to design in variable conditions on which to execute the objects... ]


I understand all the points you are making, and realize I may be trying to force a pattern when I dont really need it, depending on the need and complexity of the code requiring this functionality. And also, you could theoretically use the class in any other container without it breaking. The NEXT_LINK automatically checks to see if GetNextLink() isn't null. If you are using the object in any container or code other than a CChain<> then the next link will always be null (thanks to your protected suggestion), so the function would operate as normal reusably (if that's a word).

However, here is how I see it breaking down.

With the code I have above:

Pros:
- It's fast. It just jumps from pointer to pointer, no need for sorted trees or whatever else is executed in a stl container. [edit]Actually, it's fast as long as you are not removing links a lot, in which case you can just convert it to a doubly-linked list which would take removals from O(n) to O(1) time. So I probably will convert it.[/edit]
- The "link" can decide if execution continues down the chain, or even restarts at the beginning of the chain with a new function, or some such. Lots of stuff you can do that would take a lot more coding to do via loops, but may be easier to produce bugs (i'll list this in the cons)
- You only have one call - chain->Function(), rather than writing loops all over the place. (Which some may not consider this a pro, but I do)

Cons:
- It's intrusive in that you have to call NEXT_LINK( Function() ) if you want execution to continue down the chain. However this is by design if you read the patterns description. It's also how Sun Microsystems implemented their J2EE HttpServletFilter chain. [edit]So this may not be considered a con, however I will because it would be nice if NEXT_LINK was optional in the case where you know that execution will always execute for every link of the chain, in which case it would be handled automatically. Not sure how to do that, though..[/edit]
- If the programmer isn't careful, then there is a potential of creating bugs on how execution goes (or doesn't go) down the chain.

Does that list seem fair? It's way past my bedtime, and I'm exhausted, so there's a good chance I'm not reasoning properly, but that's nothing new anyway, hehe. Thanks again for your feedback. (Also looking forward to Kylotan's feedback)

[Edited by - Chris81 on May 18, 2005 1:01:18 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Chris81
Quote:
Original post by Telastyn
Why may I ask are you using a templated container class rather than the Standard Library's provided ones? You don't seem to be gaining anything but more code to write/debug/design.


It's the only way that I know of to accomplish the design goal. If I used a STL container, the "link" wouldn't know where it was at in the "chain" and thus couldn't call the next link's function.



And the object should never have that duty. That's something for the container to do, not the contents. You're not using any functionality that requires an object to know about it's neighbors [beyond executing the neighbor's function]. That design goal can be better accomplished with the containers already in the language.

Using STL containers, you don't need the links to run the next link's function, you just say "run every foo() in this container" and it's done.

Quote:

Quote:

Further, inappropriate use of IChainLink::SetNextLink() will result in a memory leak. It would be modestly better I think if that were private [or protected at least] and CChain made a friend of IChainLink.

Good point, thanks. I'm not sure how it would result in a memory leak, as the container is independent of the memory clean-up process as I described before. You're just passing pointers to the container, it's your responsibility to clean up the objects the pointers point to. In the example, the stack does it. In practice, ICommon or one of it's ancestors would alredy be bound to a resource management class, or you would manually clean up after yourself. Thanks again though, this is exactly the type of feedback I was hoping to get and that is still a good point because the way I had it was not properly encapsulated.


Eh, sorry, I'm not so good at reading other people's code yet. Usually linked lists like this actually allocate the container rather than inheriting from it. Changing the ->next pointer without actually deleting it [if it were allocated and not simply passed] is a no no.

Quote:

Quote:

[edit: I suppose 'icky' isn't too helpful...

It is "better" to have the individual objects be independant of their container. It allows for you to more easily change your design should requirements change. It allows you to reuse the objects in other code where the container might be subtly different. It allows you to more easily reuse different objects in this container without putting NEXT_LINK( function() )'s everywhere. It allows you to execute a subset of the container rather than always executing everything. It allows you to design in variable conditions on which to execute the objects... ]


I understand all the points you are making, and realize I may be trying to force a pattern when I dont really need it, depending on the need and complexity of the code requiring this functionality. And also, you could theoretically use the class in any other container without it breaking. The NEXT_LINK automatically checks to see if GetNextLink() isn't null. If you are using the object in any container or code other than a CChain<> then the next link will always be null (thanks to your protected suggestion), so the function would operate as normal reusably (if that's a word).

However, here is how I see it breaking down.

With the code I have above:

Pros:
- It's fast. It just jumps from pointer to pointer, no need for sorted trees or whatever else is executed in a stl container.
- The "link" can decide if execution continues down the chain, or even restarts at the beginning of the chain with a new function, or some such. Lots of stuff you can do that would take a lot more coding to do via loops, but may be easier to produce bugs (i'll list this in the cons)
- You only have one call - chain->Function(), rather than writing loops all over the place. (Which some may not consider this a pro, but I do)

Cons:
- It's intrusive in that you have to call NEXT_LINK( Function() ) if you want execution to continue down the chain. However this is by design if you read the patterns description. It's also how Sun Microsystems implemented their J2EE HttpServletFilter chain.
- If the programmer isn't careful, then there is a potential of creating bugs on how execution goes (or doesn't go) down the chain.

Does that list seem fair? It's way past my bedtime, and I'm exhausted, so there's a good chance I'm not reasoning properly, but that's nothing new anyway, hehe. Thanks again for your feedback. (Also looking forward to Kylotan's feedback)
[/quote]


Sure, you could use them in other containers, but then you've a bunch of pointers hanging around even though you've just got a stand-alone object, or have it stored in a container that uses it's own links. Not the end of the world, but...


Now to the list.

Pro 1: I doubt that this is faster than a vector<IChainLink *>. Since vectors store their members in contiguous memory [like arrays], rather than seperate memory like linked lists do, if what I hear from more experienced members is true. Also [if I hear is correct] using std::for_each will allow for some optimization under certain conditions.

Granted, I think the speed difference is slim.

Pro 2: Certainly, the example shown though doesn't utilize that sort of "break" functionality. That is easier [my gut says anyways] to impliment with this sort of list than it is with STL containers. Still, if you're using stl containers, you wouldn't [directly] use a loop.

Pro 3: Certainly. Writing the same loop over and over again is a pain, is likely to produce bugs, and is difficult to change [everywhere]. But STL containers wouldn't use a loop. The code would look something like this:

<code>
for_each( container.begin(), container.end(), conditional_functor() );
</code>

Which could be abstracted away into a function.

The conditional_functor() is the magic here. It would be a structure which executes each foo() in the container, and has all of the rules to determine whether or not to "break" the execution.

Here is a simple [and likely poor] example of something like this:


#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

struct print_only{
long count;
long limit;

template <typename T>
void operator()(T t){
if (count < limit){
cout << t << " ";
}
++count;
}

print_only(long limitation):count(0),limit(limitation){}
};

int main(){

vector<int> v(42,0); // create a vector with 42 "0"'s

for_each(v.begin(),v.end(),print_only(2));
cout << "\n";
for_each(v.begin(),v.end(),print_only(5));
cout << "\n";
}
> ./foo
0 0
0 0 0 0 0



And once again, the rather... verbose for_each call can be ferret'ed away into a function. Remember though that writing the same thing [or slightly different things] over and over is a sign that things might need designed differently.

Con 1: Indeed, I imagine that most kind of "layered filtering" is designed something like this. In that situation this sort of design is much more understandable since breaking from the execution is a likely scenario. Still, I know I wouldn't want to remember the "execute next" at the end of every foo()...

Con 2: and above.


I'm glad to provide feedback, it gets me useful practice. And it builds the confidence when more experienced programmers don't come by and tell me I'm mad. Anyways, do what you want. Getting it working is the most important thing. I'm just trying to show what is possible, and hopefully explaining somewhat vaguely how it's possible and why it's good.

Share this post


Link to post
Share on other sites
Welp, those all seem like valid points, thanks.

I think what it boils down to, unless someone else has something to add that could change my opinion, is that if you need one object in the chain to have the ability to decide if the execution continues down the chain or not, then you would use this pattern. Otherwise, it may be wiser to just use a stl container.

I believe that was the GoF's explanation of the pattern anyhow.

As a side note, I plan on using a memory manager that would allocate objects in contigous memory, so the objects that would be used in CChain would still be stored in contigous memory.

Share this post


Link to post
Share on other sites

This topic is 4598 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this