473,398 Members | 2,812 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,398 software developers and data experts.

design dilemma

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.
Jul 22 '05 #1
4 2276
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

--
"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."

Jul 22 '05 #2
On Thu, 04 Mar 2004 21:14:59 GMT, David Rubin <fu******@warpmail.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));
}
};

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.
Jul 22 '05 #3
TheFerryman wrote:
On Thu, 04 Mar 2004 21:14:59 GMT, David Rubin <fu******@warpmail.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...]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] 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."

Jul 22 '05 #4
"TheFerryman" <fe***@onthenet.com> wrote in message
news:sm********************************@4ax.com...
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;}
};
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);

//--------------------------------------------------------------------------
---------------
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.
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.

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.

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/
Jul 22 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: bartek | last post by:
Hello, I've been pondering with this for quite some time now, and finally decided to ask here for suggestions. I'm kind of confused, actually... Maybe I'm thinking too much... Brain dump...
7
by: Susan Bricker | last post by:
Greetings. As a relative newcomer to Access, I am having trouble deciding on how to design the form flow for updating and creating related records. I'm looking for a variety of suggestions so...
1
by: PCC | last post by:
I am writing a data access layer for a web service. Data from the web service is to be returned as XML. My design dilemma is this... Should I be returning XML from my stored procedures using...
0
by: Colin Basterfield | last post by:
Hi all, Not sure if this is the most appropriate place for this so apologies if inappropriate... I have come back to a framework I built some time ago to see where some refactoring can take...
3
by: reageer | last post by:
Hi all, I have a design question: I have a bunch of users (name, address, zip, etc.). They are assigned a card with a specific id. The only thing unique is this card id, or probably the...
14
by: ApexData | last post by:
In my database, I uniformly handle records on a Record by Record basis, in a single form that contains New/Edit/Del buttons. Because I'm dealing with records one at a time, I can easily manage my...
10
code green
by: code green | last post by:
I wasn't sure what title to give this, or whether I can explain my dilemma, but here goes. My company sells products that are inter-compatible with each other, mainly four ranges I will call...
2
by: =?Utf-8?B?SnVsaWEgQg==?= | last post by:
Hi, I'm wondering if anyone can give me any ideas/good practice/advice. I've got a web form which a user inputs lots of data into, then presses submit. The submit button uses two classes to input...
3
by: DerrickH | last post by:
I have a template class with three policies: PolicyA, PolicyB, and PolicyC. The design dilemma I am having is that B and C depend upon A, but I would like my Host class to derive from all three. In...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.