Getting rid of Get/Set functions

Started by
34 comments, last by snk_kid 19 years, 7 months ago
To your credit, the macros are neat.


Unfortunately, using them in the first place suggests design problems, as others have mentioned.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Advertisement
USE A CLASS TO ENFORCE INVARIANTS

IF IT'S A STRUCTURE WHOSE MEMBERS CAN HAVE ANY VALUE, MAKE IT A POD, FORCHRISSAKES

PS: You should really rename this thread to "Hiding/Obfuscating Getter/Setter functions"
daerid@gmail.com
screw get sets, just use structures!
Quote:Original post by Anonymous Poster
Wait a sec. I thought one of the primary purposes of getters/setters is to hide the implementation of the data so that now or at some future time you can change the implementation without changing the consumers of your class.

Just because there's no point at this time doesn't mean you should discard the getter/setter. It might be necessary in the future - thus the general rule to wrap it.

I'm coming from a primarily application programming background where we actually reuse our objects and code - a lot - whereas with game programming you tend to use and define it once, but I think the rule is a good one for general use.


There is a simillar thread to this going on so i'll just qoute my-self with an appropriate answer.

Quote:Original post by snk_kid
The reason why we encapsulate data meaning making data members private to enforce access and update through member functions i.e. getters & setters are:

1. You determine that your type's represenstation is variable, meaning its data members can change over-time be it the name or the type of the memeber, if you did not encapsulate the variant then clients of your type will be tightly coupled to its representation thus if your type's represenstation changes then all clients are forced to update there code to reflect the changes and then recompile it all.

By encapsulating the variant you make loose coupling by indirection, enforcing clients to access/update through an invariant interface i.e. member functions.

2. You determine that your type has a state invariant meaning that the representation has range of valid values but clients are allowed to update the representation so to enforce the state invariant you make the representation private and enforce clients to use the interface i.e. member functions to update without putting the instance of that type in an undefined state. If the represenstation is public how could you ever maintain the state invariant from clients? exactly you couldn't clients could easliy set invalid values into public data members.

So to summarize this:



  • encapsulate the variation/variant and provide invariant interface to it <-- important is the first key to abstraction


  • if your type has a state invariant then encapsulate it by making it private and enforce access/update through the interface i.e. member functions, there-for never putting an instance of that type in an undefined state.



If your type has none of these criteria then there is no reason to make it private, your type probably is not a real type and is nothing more than an aggregate/compound data structure.

A very good example of a type that does not encapsulate its data members and nor does it need to is the standard library type std::pair, a pair has 2 members and this will never change it has no state invariant to maintain there-for its members are public.

Also note if your type has lots of small fine grained accessor methods i.e getters/setters then again you've modelled your type at the wrong or mixed the levels of abstraction and you've probably got nothing more than a an aggregate/compound data structure. If its mean't to really represent a proper type then perhaps your type's representation should be grouped into new types.
Quote:Original post by Magmai Kai Holmlor
In all areas of programming, get/set tuples are a code smell. They don't do any useful work because they don't abstract anything. If they actually do something, then they are very poorly named mutators.


So the question begs to be asked: why were they introduced in the first place? Is this a case of the class designers deciding that "hey, this is a great idea, and it builds on the data hiding aspect!" (Why make a variable private? So no one can mess with it. Why make a variable protected? So your child classes can access and change it. Why make a variable public? Uh... I don't know, why?)

Second: what, exactly, would be the difference between a mutator and a "setter"? In my mind they are identical. Is there a distinction I'm missing? The purpose of a mutator is to change the variable value. The purpose of the setter is to change the variable value.

I end up using - well, properties in C# - to fire off events for variables as they're changed and to validate the information coming in. I've always assigned equal providence to properties in C# and the getter/setter pattern in C++. The only benefit properties have had is that they are invisible to the class user. Getters/Setters are not, not the way I've created and used them. (Which is greatly improved by the code samples in this thread.) Are properties more worthwhile because they're invisible? I've always recognized that you can later abstract a property into a variable's place since you can use the two interchangeably, so later data validation, event triggering, etc., can happen if needed without changing the contract proferred by the class.

Quote:Original post by Magmai Kai Holmlor
All modern C++ compilers have a property extention to make get/set function transparent and a simple template adaptor can turn a member pointer into a functor (e.g. mem_accessor(&Circle::x) ).


This I did not know. In all the time I've read and played with C++ I've never come across this factoid or, more importantly, the usage of it. That boggles my mind as properties are *nice*. :)

Thanks, as always, for the enlightening conversation.
..what we do will echo throughout eternity..
Get/Setters are only a logical abstraction to ANY form of mutators and such..

it means that, instead of exposing direct access to the variables, you should have methods that change the state, and methods, that return the state.

so you have Getters, that get information (but not GetThisVar, GetThatVar.. methods like TimeInSeconds, TimeInMinutes, etc.. or IsAlive, Count, Size, etc..)

and Setters, that set/change the object state.. (but not SetThisVar, SetThatVar.. methods like Increment, IterateToNext, ReflectTo, Invert, etc..)

but somehow, that naming to call them getters, and setters (because they get state, and set state) was shown in simple examples with real GetVar, SetVar, and the result is that bad wrong knowledge that we have today.. an abfucktion of the original purpose, wich is still a good one.
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Does GCC even support properties, however?
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
I'm getting confused about this [smile] Everyone's talking about "redesigning" the class not to use getter and setters...

Well - what about an example? Say, you want to design a Triangle class. It contains three variables that hold the position of each of the vertices (some kind of POINT structures). Then, the triangle has a color assigned to it. Also it has some nice functions to draw itself, or calucalate some things about itself. So - how could this be designed?

Oxyd
Quote:Original post by Oxyd
I'm getting confused about this [smile] Everyone's talking about "redesigning" the class not to use getter and setters...

Well - what about an example? Say, you want to design a Triangle class. It contains three variables that hold the position of each of the vertices (some kind of POINT structures). Then, the triangle has a color assigned to it. Also it has some nice functions to draw itself, or calucalate some things about itself. So - how could this be designed?

Oxyd


A Triangle is a very basic thing which doesn't need to do much work. These design principles are more for objects which actually do something... however we could stretch this a little by imagining a Triangle class which does have to do some work.

For example say you had a Triangle which was constructed by taking the length of two sides, AB and BC, the angle between them, the x and y position of their shared vertex B, and the angle of the the first side (AB) relative to some axis. This is 6 pieces of information to define a triangle.

Internally you decide to store the three vertices, you also calculate the length of the third side and store it with the others because they're useful to you.

What if you then wanted to change one of these pieces of information that you used to create the triangle? E.g. what if you want to change the length of one of the sides?

If you call SetLengthAB it will also change the length of CA. It will also change the position of A.

So GetLengthCA will return a different value before and after a call to SetLengthAB. Not particularly intuitive but correct all the same.

What you're really doing is redefining the whole triangle not just an individual length or an individual vertex. It would be clearer (if laughable), to have a function RedefineWholeTriangleUsingSideAB(length);

In a larger design it better expresses what is going on and wouldn't surprise you that repeated calls to a seemingly unrelated function, GetLengthCA, returned different values.
Quote:Original post by Magmai Kai Holmlor
In all areas of programming, get/set tuples are a code smell.

Well, what about a trivial example like this:
void SetWindowSize(int w, int h);pair<int, int> GetWindowSize();

How would you write this code?
Quote:Original post by Magmai Kai Holmlor
They don't do any useful work because they don't abstract anything.

Suppose you designed your Window class as follows. The default constructor initializes everything (x, y, width, height, title) with reasonable default values. If you call SetWindowSize() it simply changes the private members. Once you call Create(), the window is actually created. At this point if you call SetWindowSize() again, not only will it change the properties but also call appropriate code to actually modify the existing window's size. How would you go about implementing this?
Quote:Original post by Magmai Kai Holmlor
All modern C++ compilers have a property extention to make get/set function transparent and a simple template adaptor can turn a member pointer into a functor (e.g. mem_accessor(&Circle::x) ).

What are these extensions for MSVC and GCC? I couldn't find any info online. Your accessor takes care of a very dumb pair of getters/setters (still nice, but essentially just syntatic sugar). How would you implement properties that actually do something besides changing the member (say going to the database)? I suppose one could write a generic property class that takes advantage of boost's lambda library but I'm not sure how useful that would be in practice. I certainly haven't seen such implementations, it'd be cool if someone could point one out or provide a simple example.

EDIT: Also, how can you use your accessor for the above example? The function accepts two parameters (to save the user from doing make_pair) but returns a pair to avoid the ugliness of using references in order to return two values. If you extend this to an arbitrary tuple (boost's implementation, for instance), how would you implement an accessor for an arbitrary number of parameters (with specialization that returns a simple value instead of a tuple for one parameter)? Using typelists? I'm new to templates so I'm having some trouble visualizing the implementation.

EDIT: It's suprising that boost doesn't include a property implementation. Perhaps we can design one together and submit it for peer review?

This discussion is awesome [smile]

This topic is closed to new replies.

Advertisement