• Advertisement
Sign in to follow this  

Object Composition and Getter/Setters

This topic is 1656 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

Say I have a class "Connection".

A connection knows how to Read and Write.  It also needs a buffer to Read Into.  So...

class Connection
{
protected:
Reader asyncReader;
Writer asyncWriter;
DataBuffer rawData;
}

Now, say "DataBuffer" has a member called "MaxElements" that defines how many entries are in there.

If I want to change this number of elements from outside, I would essentially be doing something silly like:

AConnection->SetDataBufferSize(size)
// which in turns ends up calling
rawData->SetBufferSize(size)

With deeper nested composition, I just end up with a giant chain of set/get methods (each of which could be a source of some bizarre bug).

 

Is there a better way?

Should Reader/Writer/DataBuffer *not* be members of the connection?  This gets tricky, because then a connection can be created without resources it needs to function.  Sounds bad.

Edited by CombatWombat

Share this post


Link to post
Share on other sites
Advertisement

IMO, if you are providing getters and setters for a protected/private field and not doing anything important inside those getter/setters, you might aswell make those fields public and be done with it.

Share this post


Link to post
Share on other sites

You can't really make a buffer size public since it will need to do other stuff apart from set the member (i.e. resize the buffer and copy any unwritten/unread data to the new buffer), since you don't have properties in C++.

Share this post


Link to post
Share on other sites

One "Solution" i saw once was to use private (/protected?) inheritance, and use the "using" keyword to expose only some of the methods of the "parents"

class Connection : private DataBuffer
{
public:
    using DataBuffer::SetBufferSize;
protected:
    ...
}

Of course this might seem ugly too since it doesnt obey the "is-a" guideline of inheritance, but it is private inheritance so it shouldnt be a problem.

Share this post


Link to post
Share on other sites

I prefer to avoid inheritance when possible as it brings with it it's own set of troubles.

 

You can't really make a buffer size public since it will need to do other stuff apart from set the member

Correct.  Otherwise I wouldn't bother even making my own buffer class and would just use the underlying type (vector, circular_buffer, whatever) directly.

 

My question, I guess, is that if I wind up in a situation where the highest level "interface to the world" class has to set member variables of children's children's children: do I have a bigger design level problem on my hands?  In this particular instance it sounds perfectly reasonable for a Connection to set it's bufferSize, though.

Share this post


Link to post
Share on other sites

"AConnection->SetDataBufferSize(size)" is not the same thing with "rawData->SetBufferSize(size)".

The former is the public interface, and the later is the internal implementation.

 

Today your Connection::SetDataBufferSize forwards to DataBuffer::SetBufferSize, tomorrow you may change to that your Connection::SetDataBufferSize invokes some complicated logic other than DataBuffer::SetBufferSize. Nobody cares the changes as long as the public interface is the same.

 

So, I don't think there is any problem with your "setter" chain.

Share this post


Link to post
Share on other sites

This asinine topic, getters & setters, is precisely why I love languages with things like C#'s 'properties'.

 

Anyway, I'd argue that a substantial part of the answer to your question has to do with whether or not you're expecting 'DataBuffer' to be externally initialized & passed in to a constructor or something like that.  It seems to me that the difference that this makes is pretty substantial to the design of Connection.

 

If you're passing in a DataBuffer for Connection to use, then it seems reasonable for Connection to not even offer a setter for the buffer size at all, and instead just assume that it's size is what it should be, and provide error checks on every read/write to make sure that the DataBuffer is still as it should be.  In this case, the issue is resolved because Connection shouldn't have a setter for this at all.

 

If you're not passing in a DataBuffer, but want a user to influence the size of DataBuffer, then you're going to need the setter, and all the error handling mentioned above.  Unless of course you have the size of the DataBuffer as a constructor argument for Connection, which seems pretty reasonable to me, in which case the setter is again pointless and counter productive.

 

If you want Connection to 'just work', then you shouldn't have a setter because it allows the user to cause problems for Connection where problems just shouldn't exist.  Connection would allocate the amount of space it needs, and that's that.  The setter would once again be counter productive in this case.

 

Personally, I'd opt with either passing in a DataBuffer, or the 'just work' case.  It seems that the middle of maintaining the DataBuffer privately but still allowing for the user to muck with it is just getting all the trouble of accepting a by-reference DataBuffer and none of the implementation flexibility, and none of the usabiity of the 'just work' case.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement