View previous topic :: View next topic |
Author |
Message |
JoeCoder
Joined: 29 Oct 2005 Posts: 294
|
Posted: Wed May 14, 2008 8:50 pm Post subject: Properties vs Accessors |
|
|
Yage currently uses public properties in some places and getter setter methods (accessors) in others, and I'd like to make things consistent everywhere.
First, using public fields (and properties where necessary) requires less code and fewer methods, so I love that aspect. However, suppose SpriteNode has a public Vec3f field called scale, and ModelNode uses a get/set method for scale because it needs to update its radius when changed. This leads to the following inconsistent behavior: Code: | sprite.scale.x = 5; // works
model.scale.x = 5; // fails silently because .scale method returns a copy.
model.scale = Vec3f(model.scale.x+5, model.scale.y, model.scale.z); // clunky and inconsistent but works. |
The .scale property could return a reference to the Vec3f, but then updating x from there wouldn't go through the setter function and perform whatever it needs to do.
So, properties (scale) or accessors (setScale, getScale)? And could there be a better way that I'm missing?
Last edited by JoeCoder on Fri May 16, 2008 11:25 am; edited 1 time in total |
|
Back to top |
|
|
JoeCoder
Joined: 29 Oct 2005 Posts: 294
|
Posted: Fri May 16, 2008 11:24 am Post subject: |
|
|
I recently had an idea that would overcome the inconsistencies (outlined above) of using properties.
I could create a reusable Dirty class with methods isDirty() and setClean(). Methods like getRadius() could call isDirty() on scale and only recalculate the radius if the scale has changed. isDirty() would store its own copy of the variable from the last call to itself and do a lookup to see if it's changed. if so, getRadius can do its recalculation.
However, this method would double the memory requirements for every variable it uses, and takes longer to compare than simple dirty flags. So I think it's really just over-engineering. Unless someone has a better way, I think I'll stick with getter and setter methods.
For those that wonder why I'm going through and worrying about stuff like this now, it's been a while since I've worked on Yage and thought that doing some cleanup is a good way to refamiliarize myself with the code. |
|
Back to top |
|
|
Karynax
Joined: 21 Mar 2008 Posts: 2
|
Posted: Tue May 20, 2008 6:17 am Post subject: |
|
|
One approach to keep properties is to use a custom struct that's exposed instead of the actual struct.
In the example code below "xyz" is the normal struct.
Code: |
struct xyz {
int x;
int y;
int z;
}
|
Now suppose we have the class "Thing" that starts off like this:
Code: |
class Thing {
xyz loc;
}
|
As things evolve we decide we need to have side effects when "loc" is write or even read.
Simple approach of changing "loc" to "m_loc" and adding property methods in the place of "loc" leads to the problems you described above.
Instead we create a new struct for "loc" in the example it's called "cxyz". In this struct we hold a reference to the class it is in and property methods for each member of "xyz". "cxyz" should also have opCast() and opAssign() methed to convert to and from the "xyz" struct.
Code: |
class Thing {
private xyz m_loc;
struct cxyz {
Thing self;
int x() {
return self.m_loc.x;
}
int x(int v) {
self.m_loc.x = v;
return v;
}
int y() {
return self.m_loc.y;
}
int y(int v) {
self.m_loc.y = v;
return v;
}
int z() {
return self.m_loc.z;
}
int z(int v) {
self.m_loc.z = v;
return v;
}
xyz opCast() {
return self.m_loc;
}
void opAssign(xyz v) {
self.m_loc = v;
}
}
cxyz loc;
this() {
loc.me = this;
}
}
|
The main downside to this approach is the extra code that needs to go into each class that needs it. Templates may be useful for this though.
Another downside is the loss of access to any extra methods the normal struct has, "Vec3f" has quite a few, however this is minor as opCast() and opAssign() makes converting between the two easy and, hopefully, obvious that you're not directly manipulating the normal struct.
The isDirty() approach has the problem of adding an extra comparison of structs each time you use it. This can quickly add up if this is something polled every frame.
A variation on the isDirty() is to instead have a setDirty() function that is called by anything that touches the member variables of interest. Big downside to this is that it requires discipline and attention to do it all the time. |
|
Back to top |
|
|
JoeCoder
Joined: 29 Oct 2005 Posts: 294
|
Posted: Tue May 20, 2008 12:35 pm Post subject: |
|
|
That is an interesting approach that I hadn't thought of before, but my main reason for wanting to use more getters/setters was to reduce the lines of code.
My own idea that I came up with and rejected was because of the added complexity and performance penalty. At least your idea is more performance friendly. |
|
Back to top |
|
|
|
|
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, 2005 phpBB Group
|