FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Properties vs Accessors

 
Post new topic   Reply to topic     Forum Index -> Yage
View previous topic :: View next topic  
Author Message
JoeCoder



Joined: 29 Oct 2005
Posts: 294

PostPosted: Wed May 14, 2008 8:50 pm    Post subject: Properties vs Accessors Reply with quote

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
View user's profile Send private message
JoeCoder



Joined: 29 Oct 2005
Posts: 294

PostPosted: Fri May 16, 2008 11:24 am    Post subject: Reply with quote

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
View user's profile Send private message
Karynax



Joined: 21 Mar 2008
Posts: 2

PostPosted: Tue May 20, 2008 6:17 am    Post subject: Reply with quote

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
View user's profile Send private message
JoeCoder



Joined: 29 Oct 2005
Posts: 294

PostPosted: Tue May 20, 2008 12:35 pm    Post subject: Reply with quote

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
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic     Forum Index -> Yage All times are GMT - 6 Hours
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, 2005 phpBB Group