C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

design dilemma

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language (comp.lang.c++)
View previous topic :: View next topic  
Author Message
TheFerryman
Guest





PostPosted: Thu Mar 04, 2004 8:18 pm    Post subject: design dilemma Reply with quote



Hi,

I keep running up against the same design problem. Let's say I have a
cat object like this:

//-----------------------------------------------------------------------------------------
class Cat
{
private:

Vision* m_pVision;

Memory* m_pMemory

public:

//update vision and add all visible cats to memory
void Update(){m_pVision->Update(this);}

const Memory* GetMemory()const{return &m_pMemory;}
};

//-----------------------------------------------------------------------------------------

The Vision class looks like this:

//-----------------------------------------------------------------------------------------
class Vision
{
//returns a vector of all other cats pCat can see
vector<Cat*> GetAllOtherAnimalsInView(Cat* pCat);

void Update(Cat* pCat)
{
pCat->GetMemory()->Add(GetAllOtherAnimalsInView(pCat));
}
};

//-----------------------------------------------------------------------------------------

This is where the problem occurs, because GetMemory() must be non
const for Vision::Update to use it. And of course, if I make it non
const then there's no reason to have it private in the first place.

An alternative would be for Cat to delegate the call to Memory::Add,
by having its own Add method like so:

void Cat::Add(vector<Cat*> cats){m_pMemory->Add(cats);}

but as classes become more complex the number of extra methods I'd
have to include may get silly and I'd be better off merging the two
classes together (ie. The Memory class merged into the Cat class)

I seem to be getting this design issue fairly regularly and -- shame
on me -- I just end up creating a const and a non const version of the
call to obtain a member instance... which seems to me like very poor
design.

How should I handle this problem?

Many thanks for any feedback.
Back to top
David Rubin
Guest





PostPosted: Thu Mar 04, 2004 9:14 pm    Post subject: Re: design dilemma Reply with quote



TheFerryman wrote:

Quote:
class Cat
{
private:
Vision* m_pVision;
Memory* m_pMemory

public:
//update vision and add all visible cats to memory
void Update(){m_pVision->Update(this);}

const Memory* GetMemory()const{return &m_pMemory;}
};

[snip]
Quote:
class Vision
{
//returns a vector of all other cats pCat can see
vector<Cat*> GetAllOtherAnimalsInView(Cat* pCat);

void Update(Cat* pCat)
{
pCat->GetMemory()->Add(GetAllOtherAnimalsInView(pCat));
}
};

This is where the problem occurs, because GetMemory() must be non
const for Vision::Update to use it. And of course, if I make it non
const then there's no reason to have it private in the first place.

I think you have to define your problem in plain English. This is what I
think you want to model:

You have a class Cat. Each Cat has a Vision (a list of other Cats it can
see), and a Memory (a list of Cats that it sees and remembers?) The
method Cat::update() adds the Cat's Vision to Memory.

If this is accurate, I think you want something more like this:

class Cat
{
private:
Vision *m_vision;
Memory *m_memory;
public:
void update() { m_memory->add(m_vision); }
/* ... */
}

Where Vision has a public method which returns a vector<Cat*>, or
whatever you prefer.

If Vision and Memory do nothing more than store lists of Cat*'s, you
could define them as

std::dequeue<Cat*> m_vision;
std::dequeue<Cat*> m_memory;

(or an appropriate typedef) and then

void update() {
m_memory.insert(m_memory.end(),
m_vision.begin(),
m_vision.end());
}

/david

--
"As a scientist, Throckmorton knew that if he were ever to break wind in
the echo chamber, he would never hear the end of it."


Back to top
TheFerryman
Guest





PostPosted: Thu Mar 04, 2004 10:13 pm    Post subject: Re: design dilemma Reply with quote



On Thu, 04 Mar 2004 21:14:59 GMT, David Rubin <fullname (AT) warpmail (DOT) net>
wrote:

Quote:
TheFerryman wrote:

class Cat
{
private:
Vision* m_pVision;
Memory* m_pMemory

public:
//update vision and add all visible cats to memory
void Update(){m_pVision->Update(this);}

const Memory* GetMemory()const{return &m_pMemory;}
};

[snip]
class Vision
{
//returns a vector of all other cats pCat can see
vector<Cat*> GetAllOtherAnimalsInView(Cat* pCat);

void Update(Cat* pCat)
{
pCat->GetMemory()->Add(GetAllOtherAnimalsInView(pCat));
}
};

This is where the problem occurs, because GetMemory() must be non
const for Vision::Update to use it. And of course, if I make it non
const then there's no reason to have it private in the first place.

I think you have to define your problem in plain English. This is what I
think you want to model:

You have a class Cat. Each Cat has a Vision (a list of other Cats it can
see), and a Memory (a list of Cats that it sees and remembers?) The
method Cat::update() adds the Cat's Vision to Memory.

If this is accurate, I think you want something more like this:

class Cat
{
private:
Vision *m_vision;
Memory *m_memory;
public:
void update() { m_memory->add(m_vision); }
/* ... */
}

Where Vision has a public method which returns a vector<Cat*>, or
whatever you prefer.

If Vision and Memory do nothing more than store lists of Cat*'s, you
could define them as

std::dequeue<Cat*> m_vision;
std::dequeue<Cat*> m_memory;

(or an appropriate typedef) and then

void update() {
m_memory.insert(m_memory.end(),
m_vision.begin(),
m_vision.end());
}

/david


Hi david,

The Cat/Vision/Memory example is contrived to try to help me
illustrate the type of problem I keep coming up against. In reality
the Memory and Vision classes would have much larger interfaces... a
simple container would not be representative of this type of class at
all.

Back to top
David Rubin
Guest





PostPosted: Thu Mar 04, 2004 10:26 pm    Post subject: Re: design dilemma Reply with quote

TheFerryman wrote:
Quote:
On Thu, 04 Mar 2004 21:14:59 GMT, David Rubin <fullname (AT) warpmail (DOT) net
wrote:


TheFerryman wrote:


class Cat
{
private:
Vision* m_pVision;
Memory* m_pMemory

public:
//update vision and add all visible cats to memory
void Update(){m_pVision->Update(this);}

const Memory* GetMemory()const{return &m_pMemory;}
};

[snip]

class Vision
{
//returns a vector of all other cats pCat can see
vector<Cat*> GetAllOtherAnimalsInView(Cat* pCat);

void Update(Cat* pCat)
{
pCat->GetMemory()->Add(GetAllOtherAnimalsInView(pCat));
}
};

[snip - try...]
Quote:
class Cat
{
private:
Vision *m_vision;
Memory *m_memory;
public:
void update() { m_memory->add(m_vision); }
/* ... */
}

Where Vision has a public method which returns a vector<Cat*>, or
whatever you prefer.

[snip - Vision/Memory are simple containers]
Quote:
The Cat/Vision/Memory example is contrived to try to help me
illustrate the type of problem I keep coming up against. In reality
the Memory and Vision classes would have much larger interfaces... a
simple container would not be representative of this type of class at
all.

I didn't really think they were. In any case, The interface you
presented seems to take a very roundabout route to get a Cat's Vision
into its Memory. I'd be interested to know whether the simplified
interface suggested above meets your requirements. As I said, I had
trouble understanding exactly what you are trying to model.

/david

--
"As a scientist, Throckmorton knew that if he were ever to break wind in
the echo chamber, he would never hear the end of it."


Back to top
Cy Edmunds
Guest





PostPosted: Fri Mar 05, 2004 6:00 am    Post subject: Re: design dilemma Reply with quote

"TheFerryman" <ferry (AT) onthenet (DOT) com> wrote

Quote:
Hi,

I keep running up against the same design problem. Let's say I have a
cat object like this:


//--------------------------------------------------------------------------

---------------
Quote:
class Cat
{
private:

Vision* m_pVision;

Memory* m_pMemory

public:

//update vision and add all visible cats to memory
void Update(){m_pVision->Update(this);}

const Memory* GetMemory()const{return &m_pMemory;}
};

This design doesn't make sense to me. Look at the public interface:

class Cat
{
public:
void Update();
const Memory *GetMemory() const;
};

Neither method takes an argument! How can Update do anything useful without
input? C++ classes are not so intuitive.

Adding an accessor to a (non-const) Memory * would allow you to code
Update(), but the fact that it depends on a vector of Cat pointers would be
concealed from the client. Unless you are implementing some design pattern
which requires such trickery, I would propose the simpler:

void Update(const std::vector<Cat*> &);

or even (since Update isn't virtual) the more flexible

template <typename CAT_ITER>
void Update(CAT_ITER first_cat, CAT_ITER last_cat);

Quote:


//--------------------------------------------------------------------------

---------------
Quote:

The Vision class looks like this:


//--------------------------------------------------------------------------

---------------
Quote:
class Vision
{
//returns a vector of all other cats pCat can see
vector<Cat*> GetAllOtherAnimalsInView(Cat* pCat);

void Update(Cat* pCat)
{
pCat->GetMemory()->Add(GetAllOtherAnimalsInView(pCat));
}
};


//--------------------------------------------------------------------------

---------------
Quote:

This is where the problem occurs, because GetMemory() must be non
const for Vision::Update to use it. And of course, if I make it non
const then there's no reason to have it private in the first place.

Why do you say that? If you make m_pMemory public anybody could change the
actual value of the pointer. Ouch. If you add

Memory* GetMemory();

they could only change what the pointer points to. That's a much less
drastic step.

Quote:

An alternative would be for Cat to delegate the call to Memory::Add,
by having its own Add method like so:

void Cat::Add(vector<Cat*> cats){m_pMemory->Add(cats);}

but as classes become more complex the number of extra methods I'd
have to include may get silly and I'd be better off merging the two
classes together (ie. The Memory class merged into the Cat class)

Well, I can't tell from your description what form this added complexity
will take. The usual way to make things extendable in C++ is to defer as
much as possible to virtual functions. But it's not the only way. I
recommend you have a long look at the Design Patterns book.

Quote:

I seem to be getting this design issue fairly regularly and -- shame
on me -- I just end up creating a const and a non const version of the
call to obtain a member instance... which seems to me like very poor
design.

Actually it's pretty common. What is says is, "If you have full access to
me, you get full access to my buddy over here too. But if you only have read
access to me you only get read access to my buddy." I wouldn't necessarily
call that poor design although in some cases it might not be appropriate of
course.

[snip]

--
Cy
http://home.rochester.rr.com/cyhome/



Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language (comp.lang.c++) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.