Data encapsulation

Started by
5 comments, last by All8Up 12 years, 10 months ago
Hi all.
I am a beginning programmer writing a basic game engine. I am confused about data encapsulation within my programme, so I thought I would ask for advice on this forum. I have created a Property class that represents a basic "name, value" pair. Additionally, the "value" can be a list of Property objects in it's own right, allowing a single list of Property objects to represent a tree of "name, value" pairs. The Property class also provides a method that iterates recursively through the tree structure and saves the tree data in a to a file (currently XML). I am planning on using this system to store properties of objects within the game, but also game settings and other such disparate applications. Essentially, the "parent" object that contains a tree of Property objects should be able to modify the values of the Property objects at will (e.g. to update character health after a hit). I have avoided using getter and setter methods in my code thus far, but I am at a loss as to how I should provide access to the value of Property objects in an encapsulated way without them. How would you go about providing access to the information in the Property object without compromising code re-usability?
Thanks,
Mike.
Advertisement
Depends :-)

What language are you using?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Hi all.
I am a beginning programmer writing a basic game engine. I am confused about data encapsulation within my programme, so I thought I would ask for advice on this forum. I have created a Property class that represents a basic "name, value" pair. Additionally, the "value" can be a list of Property objects in it's own right, allowing a single list of Property objects to represent a tree of "name, value" pairs. The Property class also provides a method that iterates recursively through the tree structure and saves the tree data in a to a file (currently XML). I am planning on using this system to store properties of objects within the game, but also game settings and other such disparate applications. Essentially, the "parent" object that contains a tree of Property objects should be able to modify the values of the Property objects at will (e.g. to update character health after a hit). I have avoided using getter and setter methods in my code thus far, but I am at a loss as to how I should provide access to the value of Property objects in an encapsulated way without them. How would you go about providing access to the information in the Property object without compromising code re-usability?
Thanks,
Mike.


I'm going to be very negative and say don't do this in the object system. This sounds like a fine system for configuration and settings but the overhead of such a generic system used in simulated objects (i.e. those that have health) will quickly become unreasonable if you go over a couple hundred objects being simulated per frame. I'd look at a dynamic yet compile time known solution instead of this for any of the object data because the cost is nearly exponential as you grow object types and counts.
Thank you both for the replies.
Apoch: I am using C++, sorry about that omission :P
AllEightUp: I had considered that performance would be an issue, so I had planned on implementing a fixed size list of Properties for in-game objects (say 200 per object). All variably sized Property s (like current inventory contents) would then be sublists of the main list. I would then use a simple numerical index system to access the properties (e.g. the 8th entry in the main list is always current health, 100th entry is always a Property list containing the contents of the inventory etc.). Surely the overhead for such a system would just be the cost of accessing an element of an array?
Your most straightforward option is probably to implement a generic get/set on the Property class, and have it traverse the property tree looking for the appropriate value to retrieve or modify:

[source lang="cpp"]bool Property::HasProperty(const PropertyID& id) const // Can pass PropertyIDs by value if they're just enums or ints or whatever
{
std::map<PropertyID, PropertyValueType>::const_iterator iter = DirectProperties.find(id);
if(iter != DirectProperties.end())
return true;


for(std::list<Property>::const_iterator treeiter = PropertyTree.begin(), treeend = PropertyTree.end(); treeiter != treeend; ++treeiter)
{
if(iter->HasProperty(id))
return true;
}

return false;}

template <typename T>
T Property::Read(const PropertyID& id) const
{
std::map<PropertyID, PropertyValueType>::const_iterator iter = DirectProperties.find(id);
if(iter != DirectProperties.end())
return static_cast<T>(iter->second); // Might need a more sophisticated conversion depending on what your underlying PropertyValueType is

for(std::list<Property>::const_iterator treeiter = PropertyTree.begin(), treeend = PropertyTree.end(); treeiter != treeend; ++treeiter)
{
if(iter->HasProperty(id))
return iter->Read<T>(id);
}

throw std::exception("Invalid property ID");
}

template <typename T>
void Property::Write(const PropertyID& id, const T& value)
{
// Traverse the tree *first* so we guarantee that the correct property is found and set before setting it on this node

for(std::list<Property>::iterator treeiter = PropertyTree.begin(), treeend = PropertyTree.end(); treeiter != treeend; ++treeiter)
{
if(iter->HasProperty(id))
{
iter->Write(id, value);
return;
}
}

DirectProperties[id] = static_cast<PropertyValueType>(value); // again, conversion may need to be more sophisticated here
}[/source]

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Have you considered offloading some of this to a scripting language? They often represent "objects" as recursive dictionaries, with some neat optimisations (e.g. special handling for dictionaries less than a given size). It might also save you from duplicating the data in XML, as you would be able to write your object configuration in the scripts themselves. For example, you might use the prototype pattern for objects.

Thank you both for the replies.
Apoch: I am using C++, sorry about that omission :P
AllEightUp: I had considered that performance would be an issue, so I had planned on implementing a fixed size list of Properties for in-game objects (say 200 per object). All variably sized Property s (like current inventory contents) would then be sublists of the main list. I would then use a simple numerical index system to access the properties (e.g. the 8th entry in the main list is always current health, 100th entry is always a Property list containing the contents of the inventory etc.). Surely the overhead for such a system would just be the cost of accessing an element of an array?


The index system is not a bad one for performance but it does come at the cost that such a fixed scheme is often limiting and will always "grow" over time (you'll always want to use more properties) making it more and more expensive in memory size. I personally still like data block systems which are a cross between the property scheme and the index scheme. Basically it works something like:


class MyObject : public Go::Object
{
public:
.. whatever needed ..

// up in "Go::Object" I have functions:
PropRef< int > FindProperty( const std::string& name );

static void InitializeProperties()
{
mWhateverProperty = FindProperty( "Health" );
}
virtual bool Update( const float deltaT )
{
// Use it just like an int here.
mWhateverProperty( this ) += 1;
return true;
}

private:
// Handles to properties. Can be full structures if desired since the propref is a transparent passthrough.
static PropRef< int > mWhateverProperty;
};


So, basically this is the same overhead as using the index solution you mention (except the one time lookup of the PropRef) but uses only the amount of memory space actually required. They are pretty tricky to implement the first time but once you figure it out, it's a very fast and effective method of mixing the concept of dynamic properties into the system without the overhead of a flat fixed sized item.

This topic is closed to new replies.

Advertisement